/**
 @file sys_usw_nexthop_overlay.c

 @date 2019-03-14

 @version v2.0

 The file contains all nexthop overlay tunnel related callback function
*/

/****************************************************************************
 *
* Header Files
*
****************************************************************************/
#include "ctc_const.h"
#include "ctc_error.h"
#include "ctc_linklist.h"

#include "sys_usw_common.h"
#include "sys_usw_internal_port.h"
#include "sys_usw_port.h"
#include "sys_usw_nexthop_api.h"
#include "sys_usw_nexthop_hw.h"
#include "sys_usw_nexthop.h"
#include "sys_usw_vlan.h"
#include "sys_usw_stats_api.h"
#include "sys_usw_opf.h"
#include "sys_usw_l3if.h"
#include "sys_usw_register.h"
#include "sys_usw_wb_nh.h"
#include "drv_api.h"
//#include "usw/include/drv_io.h"

/****************************************************************************
 *
* Defines and Macros
*
*****************************************************************************/


#define SYS_NH_OVERLAY_MIN_INNER_FID      (0x1FFE)   /*reserve fid*/
#define SYS_NH_OVERLAY_MAX_INNER_FID      (0x3FFE)

#define SYS_NH_OVERLAY_MAX_DSCP_VALUE  63

#define INTERCEPT_IPV6_ADDR(ip, len)                    \
{                                                       \
    uint8 sublen = len % 32;                            \
    int8 index = len / 32;                              \
    if (sublen)                                         \
    {                                                   \
        (ip)[index] >>= 32 - sublen;                    \
        (ip)[index] <<= 32 - sublen;                    \
        index += 1;                                     \
    }                                                   \
    for (; index < 4; index++)                          \
    {                                                   \
        (ip)[index] = 0;                                \
    }                                                   \
}

#define ROT(x, k) (((x) << (k)) | ((x) >> (32 - (k))))

#define MIX(a, b, c) \
    do \
    { \
        a -= c;  a ^= ROT(c, 4);  c += b; \
        b -= a;  b ^= ROT(a, 6);  a += c; \
        c -= b;  c ^= ROT(b, 8);  b += a; \
        a -= c;  a ^= ROT(c, 16);  c += b; \
        b -= a;  b ^= ROT(a, 19);  a += c; \
        c -= b;  c ^= ROT(b, 4);  b += a; \
    } while (0)

#define SYS_ISOLATION_ID_VALID(id)  ( \
    !CTC_FLAG_ISSET(p_ol_tunnel->flag, CTC_NH_OL_TUNNEL_FLAG_ISOLATION_EN) \
    || (id > 0 && id <= SYS_ISOLATION_ID_MAX) \
    || 0xFFFF == id \
    )
extern int32 _sys_usw_nh_ip_tunnel_alloc_ipsa_idx(uint8 lchip, uint8 ip_ver, void* p_add, uint8* p_index, uint8 old_index);
extern int32 _sys_usw_nh_ip_tunnel_free_ipsa_idx(uint8 lchip, uint8 ip_ver, uint8 index);
STATIC int32
_sys_usw_nh_overlay_add_dsfwd(uint8 lchip, sys_nh_info_com_t* p_com_db, uint8 is_dsfwd1);


/*Update ldp for nexthop ldp tunnel underlay change*/
int32
sys_usw_nh_update_overaly(uint8 lchip, sys_com_ldp_update_t* p_param, sys_nh_db_ol_tunnel_t* tunnel_db)
{
    sys_nh_info_overlay_t *p_ol_nh_db = NULL;
    sys_nh_info_ext_t* p_nh_info_ext = NULL;
    sys_nh_ref_list_node_t* p_curr = NULL;


    SYS_USW_REGISTER_WB_SYNC_EN(lchip, CTC_FEATURE_NEXTHOP, SYS_WB_APPID_NH_SUBID_INFO_COM, 1);

    /*update overaly*/
    p_curr = tunnel_db->p_ref_nh_list;
    while (p_curr)
    {
        p_ol_nh_db = (sys_nh_info_overlay_t*)p_curr->p_ref_nhinfo;

        CTC_ERROR_RETURN(sys_usw_nh_get_nhinfo_ext_by_nhid(lchip, p_ol_nh_db->hdr.nh_id, &p_nh_info_ext, 0));

        SYS_NH_DBG_OUT(CTC_DEBUG_LEVEL_INFO, "update ldp list, nhid:%d\n", p_ol_nh_db->hdr.nh_id);

        /*update overlay dsfwd*/
        p_ol_nh_db->gport = tunnel_db->gport;
        if (DRV_FROM_AT(lchip) && SYS_NH_OVERLAY_NEW_MODE(lchip))
        {
            if (tunnel_db->ul_nh_en)
            {
                sys_nh_info_dsnh_t nh_info;
                sal_memset(&nh_info, 0, sizeof(sys_nh_info_dsnh_t));
                CTC_ERROR_RETURN(_sys_usw_nh_get_nhinfo(lchip, tunnel_db->ul_nhid, &nh_info));
                p_ol_nh_db->hdr.dsfwd1_offset = nh_info.dsfwd1_offset;
            }
            else
            {
                p_ol_nh_db->hdr.dsfwd1_offset = tunnel_db->ul_nhid;
            }

            if (CTC_FLAG_ISSET(tunnel_db->flag, SYS_NH_OL_TUNNEL_FLAG_DSFWD))
            {
                CTC_SET_FLAG(p_ol_nh_db->hdr.nh_entry_flags, SYS_NH_INFO_FLAG_HAVE_DSFWD1);
            }
            else
            {
                 CTC_UNSET_FLAG(p_ol_nh_db->hdr.nh_entry_flags, SYS_NH_INFO_FLAG_HAVE_DSFWD1);
            }
        }
        if (tunnel_db->ecmp_if_id)/*update to another ecmp if*/
        {
            sys_l3if_ecmp_if_t ecmp_if = {0};
            CTC_ERROR_RETURN(sys_usw_l3if_get_ecmp_if_info(lchip, tunnel_db->ecmp_if_id, &ecmp_if));
            p_ol_nh_db->ecmp_gid = ecmp_if.hw_group_id;
        }
        else
        {
            if (tunnel_db->ul_nh_en)
            {
                sys_nh_info_ecmp_t *p_ecmp_db = NULL;
                sys_nh_info_com_t*p_nhinfo = NULL;

                CTC_ERROR_RETURN(sys_usw_nh_get_nhinfo_by_nhid(lchip, tunnel_db->ul_nhid, &p_nhinfo));
                if (SYS_NH_TYPE_ECMP == p_nhinfo->hdr.nh_entry_type)/*underlay nh is ecmp*/
                {
                    p_ecmp_db = (sys_nh_info_ecmp_t*) p_nhinfo;
                    p_ol_nh_db->ecmp_gid = p_ecmp_db->ecmp_group_id;
                    CTC_SET_FLAG(p_ol_nh_db->hdr.nh_entry_flags, SYS_NH_INFO_FLAG_USE_ECMP_IF);
                }
                else/*underlay nh is ipuc*/
                {
                    p_ol_nh_db->ecmp_gid = 0;
                    CTC_UNSET_FLAG(p_ol_nh_db->hdr.nh_entry_flags, SYS_NH_INFO_FLAG_USE_ECMP_IF);
                }
            }
            else/*underlay is arp*/
            {
                p_ol_nh_db->ecmp_gid = 0;
                CTC_UNSET_FLAG(p_ol_nh_db->hdr.nh_entry_flags, SYS_NH_INFO_FLAG_USE_ECMP_IF);
            }

            /*update dsfwd*/
            
            if (CTC_FLAG_ISSET(p_ol_nh_db->hdr.nh_entry_flags, SYS_NH_INFO_FLAG_HAVE_DSFWD)
                || CTC_FLAG_ISSET(p_ol_nh_db->hdr.nh_entry_flags, SYS_NH_INFO_FLAG_HAVE_DSFWD1))
            {
                sys_fwd_t sys_fwd;
                uint32 dsfwd_offset = 0;

                sal_memset(&sys_fwd, 0, sizeof(sys_fwd));
                dsfwd_offset = DRV_FROM_AT(lchip)? p_ol_nh_db->hdr.dsfwd1_offset : p_ol_nh_db->hdr.dsfwd_offset;

                sys_fwd.offset = dsfwd_offset;
                sys_fwd.is_dsfwd1 = CTC_FLAG_ISSET(p_ol_nh_db->hdr.nh_entry_flags, SYS_NH_INFO_FLAG_HAVE_DSFWD1);
                sys_usw_nh_read_asic_table(lchip, sys_fwd.is_dsfwd1? SYS_NH_ENTRY_TYPE_FWD_HALF1 : SYS_NH_ENTRY_TYPE_FWD_HALF, dsfwd_offset, &sys_fwd);
                if (!DRV_FROM_AT(lchip) ||!SYS_NH_OVERLAY_NEW_MODE(lchip))
                {
                    sys_fwd.dest_map = p_param->destmap;
                }
                else
                {
                    sys_fwd.nexthop_ptr = p_ol_nh_db->hdr.dsnh_offset;
                    sys_fwd.bypass_igs_edit = CTC_FLAG_ISSET(p_ol_nh_db->hdr.nh_entry_flags, SYS_NH_INFO_FLAG_EGRESS_EDIT_MODE);
                }
                CTC_ERROR_RETURN(sys_usw_nh_write_asic_table(lchip, sys_fwd.is_dsfwd1? SYS_NH_ENTRY_TYPE_FWD1 : SYS_NH_ENTRY_TYPE_FWD, dsfwd_offset, &sys_fwd));
            }
            else if (!DRV_FROM_AT(lchip) ||!SYS_NH_OVERLAY_NEW_MODE(lchip))
            {
                if (p_nh_info_ext && p_nh_info_ext->ecmp_list)
                {
                    if (CTC_E_NONE != _sys_usw_nh_overlay_add_dsfwd(lchip, (sys_nh_info_com_t*)p_ol_nh_db, DRV_FROM_AT(lchip)))
                    {
                        p_curr = p_curr->p_next;
                        continue;
                    }
                }
            }
        }


        /*update overlay ecmp(First level)*/
        if (p_nh_info_ext && p_nh_info_ext->ecmp_list)
        {
            sys_usw_nh_update_ecmp_member(lchip, p_ol_nh_db->hdr.nh_id, SYS_NH_CHANGE_TYPE_UPDATE_LDP, p_nh_info_ext->ecmp_list);
        }
        p_curr = p_curr->p_next;

    }


    return 0;
}

STATIC int32
_sys_usw_nh_overlay_add_dsfwd(uint8 lchip, sys_nh_info_com_t* p_com_db, uint8 is_dsfwd1)
{
    sys_nh_info_overlay_t* p_nh_info = NULL;
    sys_nh_param_dsfwd_t dsfwd_param;
    sys_nh_db_ol_tunnel_t* tunnel_db = NULL;
    int32 ret = CTC_E_NONE;

    p_nh_info     = (sys_nh_info_overlay_t*)(p_com_db);
    tunnel_db = _sys_usw_nh_lkup_tunnel(lchip, p_nh_info->dest_logic_port);
    if (tunnel_db == NULL)
    {
        SYS_NH_DBG_OUT(CTC_DEBUG_LEVEL_ERROR, " [Nexthop] overlay tunnel not exist!\n");
        return CTC_E_NOT_EXIST;
    }

    sal_memset(&dsfwd_param, 0, sizeof(sys_nh_param_dsfwd_t));
    dsfwd_param.is_mcast = FALSE;
    dsfwd_param.nexthop_ext = CTC_FLAG_ISSET(p_nh_info->hdr.nh_entry_flags, SYS_NH_INFO_FLAG_USE_DSNH8W);
    if (CTC_FLAG_ISSET(tunnel_db->flag, SYS_NH_OL_TUNNEL_REROUTE))
    {
        sys_usw_get_gchip_id(lchip, &dsfwd_param.dest_chipid);
    }
    else
    {
        dsfwd_param.dest_chipid = SYS_MAP_CTC_GPORT_TO_GCHIP(p_nh_info->gport);
    }

    if (tunnel_db->ul_nhid && tunnel_db->ul_nh_en)
    {
        sys_nh_info_com_t *p_ul_nh_info = NULL;

        CTC_ERROR_RETURN(sys_usw_nh_get_nhinfo_by_nhid(lchip, tunnel_db->ul_nhid, (sys_nh_info_com_t**)&p_ul_nh_info));
        if (p_ul_nh_info && SYS_NH_TYPE_ECMP == p_ul_nh_info->hdr.nh_entry_type)
        {
            sys_nh_info_ecmp_t *p_ecmp_nh = NULL;
            p_ecmp_nh = (sys_nh_info_ecmp_t*)p_ul_nh_info;
            dsfwd_param.dest_id = p_ecmp_nh->ecmp_group_id;
            dsfwd_param.is_ecmp = 1;
        }
    }
    else if (tunnel_db->arp_id)
    {
        sys_nh_db_arp_t *p_arp = NULL;
        p_arp = (sys_usw_nh_lkup_arp_id(lchip, tunnel_db->arp_id));
        if (NULL == p_arp)
        {
            return CTC_E_NOT_EXIST;
        }
        dsfwd_param.dest_id = p_arp->destmap_profile;
        dsfwd_param.use_destmap_profile = 1;
    }
    else
    {
        dsfwd_param.dest_id = SYS_MAP_CTC_GPORT_TO_DRV_LPORT(p_nh_info->gport);
    }
    dsfwd_param.cut_through_dis = 1;/*disable cut through for non-ip packet when encap ip-tunnel*/
    dsfwd_param.cloud_sec_en = tunnel_db->dot1ae_en;
    /*2.2 Build DsFwd Table*/
    dsfwd_param.dsnh_offset = p_nh_info->hdr.dsnh_offset;
    dsfwd_param.is_egress_edit =  CTC_FLAG_ISSET(p_nh_info->hdr.nh_entry_flags, SYS_NH_INFO_FLAG_EGRESS_EDIT_MODE);

     /*2.3 alloc offset and write table dsfwd*/
    CTC_ERROR_RETURN(_sys_usw_nh_add_dsfwd(lchip, p_com_db, &dsfwd_param, is_dsfwd1));

    return ret;
}


STATIC int32
_sys_usw_nh_tunnel_build_dsl3edit_prepare(uint8 lchip, ctc_nh_ol_tunnel_t* p_ol_tunnel,
                                              sys_nh_db_ol_tunnel_t* tunnel_db, sys_ldp_t* sys_tunnel)
{
    if(p_ol_tunnel->dot1ae_chan_id)
    {
        sys_com_dot1ae_bind_sc_t bind_sc;
        uint32 derive_mode = 0;

        if (tunnel_db->dot1ae_en && (tunnel_db->dot1ae_channel != p_ol_tunnel->dot1ae_chan_id))
        {
            SYS_NH_DBG_OUT(CTC_DEBUG_LEVEL_ERROR, "Dot1ae channel have already bind! \n");
            return CTC_E_INVALID_CONFIG;
        }
        else if(tunnel_db->dot1ae_en && (tunnel_db->dot1ae_channel == p_ol_tunnel->dot1ae_chan_id))
        {
            return CTC_E_NONE;
        }

        sal_memset(&bind_sc,0,sizeof(bind_sc));
        bind_sc.type = 1;
        bind_sc.dir = 0;  /*tx*/
        bind_sc.gport = tunnel_db->logic_port;

        CTC_ERROR_RETURN(sys_usw_global_ctl_get(lchip, CTC_GLOBAL_NH_ARP_MACDA_DERIVE_MODE, &derive_mode));

        if (derive_mode == CTC_GLOBAL_MACDA_DERIVE_FROM_NH_ROUTE2)
        {
            SYS_NH_DBG_OUT(CTC_DEBUG_LEVEL_ERROR, "Nexthop derive mode conflict with tunnel dot1ae! \n");
            return CTC_E_INVALID_CONFIG;
        }

        bind_sc.chan_id = p_ol_tunnel->dot1ae_chan_id;
        CTC_ERROR_RETURN(SYS_CB_RET(CTC_E_NOT_SUPPORT, SYS_CB_DOT1AE_BIND_SEC_CHAN, lchip,&bind_sc));

        tunnel_db->dot1ae_en = 1;
        tunnel_db->dot1ae_channel = p_ol_tunnel->dot1ae_chan_id;
        tunnel_db->sc_index =  (bind_sc.sc_index << (DRV_FROM_TMM(lchip)? 0 : 2));
        tunnel_db->sci_en   = bind_sc.include_sci;
        tunnel_db->is_4x = 1;
    }
    else if (tunnel_db->dot1ae_en)
    {
        sys_com_dot1ae_bind_sc_t bind_sc;
        sal_memset(&bind_sc,0,sizeof(bind_sc));
        bind_sc.type = 1;
        bind_sc.dir = 0;  /*tx*/
        bind_sc.gport = tunnel_db->logic_port;
        bind_sc.sc_index= (tunnel_db->sc_index >> (DRV_FROM_TMM(lchip)? 0 : 2));

        CTC_ERROR_RETURN(SYS_CB_RET(CTC_E_NOT_SUPPORT, SYS_CB_DOT1AE_UNBIND_SEC_CHAN, lchip,&bind_sc));
        tunnel_db->dot1ae_en = 0;
        tunnel_db->dot1ae_channel = 0;
        tunnel_db->sc_index = 0;
        tunnel_db->sci_en = 0;
        tunnel_db->is_4x = 0;
    }

    return CTC_E_NONE;
}

#define OVERLAY_TUNNEL
STATIC int32
_sys_usw_nh_tunnel_build_vxlan_edit_profile(uint8 lchip, ctc_nh_ol_tunnel_t* p_ol_tunnel, sys_nh_db_ol_tunnel_t* tunnel_db)
{
    uint32 cmd;
    ParserLayer4AppCtl_m app_ctl;
    sys_nh_vxlan_hw_profile_t vxlan_profile;

     if(p_ol_tunnel->tunnel_type == CTC_NH_OL_TUNNEL_TYPE_VXLAN
         && !CTC_FLAG_ISSET(p_ol_tunnel->flag, CTC_NH_OL_TUNNEL_FLAG_VXLAN_GPE)
         &&  !CTC_FLAG_ISSET(p_ol_tunnel->flag, CTC_NH_OL_TUNNEL_FLAG_VXLAN_GBP)
           && !p_ol_tunnel->vxlan_flags)
     { /*use reserved profile 0*/
        tunnel_db->vxlan_edit_idx = 0;
         return CTC_E_NONE;
     }
    sal_memset(&vxlan_profile, 0, sizeof(vxlan_profile));
    cmd = DRV_IOR(ParserLayer4AppCtl_t, DRV_ENTRY_FLAG);
    DRV_IOCTL(lchip, 0, cmd, &app_ctl);

    if(p_ol_tunnel->tunnel_type == CTC_NH_OL_TUNNEL_TYPE_GENEVE)
    {
        vxlan_profile.udp_dest_port = GetParserLayer4AppCtl(V, geneveVxlanUdpDestPort_f, &app_ctl);
        vxlan_profile.rsv3_as_etype = 1;
    }
    else
    {
        vxlan_profile.udp_dest_port = GetParserLayer4AppCtl(V, vxlanUdpDestPort_f, &app_ctl);
        if(CTC_FLAG_ISSET(p_ol_tunnel->flag, CTC_NH_OL_TUNNEL_FLAG_VXLAN_GPE))
        {
            vxlan_profile.rsv3_as_protocol = 1;
            vxlan_profile.flags = 0xC;
        }
        else
        {
            vxlan_profile.with_policy_group = CTC_FLAG_ISSET(p_ol_tunnel->flag, CTC_NH_OL_TUNNEL_FLAG_VXLAN_GBP)?1:0;
            vxlan_profile.flags = p_ol_tunnel->vxlan_flags ? p_ol_tunnel->vxlan_flags : \
                (CTC_FLAG_ISSET(p_ol_tunnel->flag, CTC_NH_OL_TUNNEL_FLAG_VXLAN_GBP)?0x88:0x08);
            vxlan_profile.rsv1 = p_ol_tunnel->vxlan_rsv1>>16;
            vxlan_profile.rsv2 = (p_ol_tunnel->vxlan_rsv1>>8)&0xFF;
            vxlan_profile.rsv3 = (p_ol_tunnel->vxlan_rsv1)&0xFF;
            vxlan_profile.rsv7 = p_ol_tunnel->vxlan_rsv2;
        }
    }
    CTC_ERROR_RETURN(sys_usw_nh_add_vxlan_edit_profile(lchip, &vxlan_profile));
    tunnel_db->vxlan_edit_idx = vxlan_profile.profile_idx;
    return CTC_E_NONE;
}
STATIC int32
_sys_usw_nh_tunnel_build_dsl3edit_v4(uint8 lchip, ctc_nh_ol_tunnel_t* p_ol_tunnel,
                                              sys_nh_db_ol_tunnel_t* tunnel_db, sys_ldp_t* sys_tunnel)
{
    sys_dsl3edit_tunnelv4_t  dsl3edit4w;
    int32 ret = CTC_E_NONE;

    sal_memset(&dsl3edit4w, 0, sizeof(dsl3edit4w));
    dsl3edit4w.l3_rewrite_type = tunnel_db->is_4x? DRV_ENUM(DRV_L3EDITTYPE_ADDIP44X) : DRV_ENUM(DRV_L3EDITTYPE_ADDIP4);
    dsl3edit4w.ds_type   = SYS_NH_DS_TYPE_L3EDIT;

    if (p_ol_tunnel->dot1ae_chan_id)
    {
        dsl3edit4w.cloud_sec_en = 1;
        dsl3edit4w.sc_index = tunnel_db->sc_index;
        dsl3edit4w.sci_en = tunnel_db->sci_en;
    }

    /*ttl*/
    dsl3edit4w.ttl = p_ol_tunnel->ttl;
    dsl3edit4w.map_ttl = CTC_FLAG_ISSET(p_ol_tunnel->flag,
                                        CTC_NH_OL_TUNNEL_FLAG_MAP_TTL);
    if (p_ol_tunnel->ecn_select ==  CTC_NH_ECN_SELECT_NONE)
    {
        dsl3edit4w.ecn_mode = 3;
    }
    else if (p_ol_tunnel->ecn_select ==  CTC_NH_ECN_SELECT_ASSIGN)
    {
        return CTC_E_NOT_SUPPORT;
    }
    else if (p_ol_tunnel->ecn_select ==  CTC_NH_ECN_SELECT_MAP)
    {
        dsl3edit4w.ecn_mode = 1;
    }
    else if (p_ol_tunnel->ecn_select ==  CTC_NH_ECN_SELECT_PACKET)
    {
        dsl3edit4w.ecn_mode = 0;
    }

    /*stats*/
    if (p_ol_tunnel->stats_id)
    {
        CTC_ERROR_RETURN(sys_usw_flow_stats_get_statsptr(lchip,
                                                           p_ol_tunnel->stats_id,
                                                           &(dsl3edit4w.stats_ptr)));
    }

    if (CTC_FLAG_ISSET(p_ol_tunnel->flag, CTC_NH_OL_TUNNEL_FLAG_COPY_DONT_FRAG))
    {
        dsl3edit4w.copy_dont_frag = 1;
        dsl3edit4w.dont_frag  = 0;
    }
    else
    {
        dsl3edit4w.copy_dont_frag = 0;
        dsl3edit4w.dont_frag = CTC_FLAG_ISSET(p_ol_tunnel->flag, CTC_NH_OL_TUNNEL_FLAG_DONT_FRAG);
    }

    /* dscp */
    if (p_ol_tunnel->dscp_select == CTC_NH_DSCP_SELECT_ASSIGN)
    {
        CTC_MAX_VALUE_CHECK(p_ol_tunnel->dscp_or_tos, SYS_NH_OVERLAY_MAX_DSCP_VALUE);
        dsl3edit4w.derive_dscp = 1;
        dsl3edit4w.dscp = p_ol_tunnel->dscp_or_tos;
    }
    else if (p_ol_tunnel->dscp_select == CTC_NH_DSCP_SELECT_MAP)
    {
        dsl3edit4w.derive_dscp = 2;
        dsl3edit4w.dscp_domain = p_ol_tunnel->dscp_domain;
    }
    else if (p_ol_tunnel->dscp_select == CTC_NH_DSCP_SELECT_PACKET)
    {
        dsl3edit4w.derive_dscp = 3;
    }
    else if (p_ol_tunnel->dscp_select == CTC_NH_DSCP_SELECT_NONE)
    {
        dsl3edit4w.derive_dscp = 0;
    }
    else
    {
        return CTC_E_INVALID_PARAM;
    }

    if (CTC_FLAG_ISSET(p_ol_tunnel->flag, CTC_NH_OL_TUNNEL_FLAG_MTU_CHECK))
    {
        if (p_ol_tunnel->mtu_size > 0x3FFF)
        {
            return CTC_E_INVALID_PARAM;
        }

        dsl3edit4w.mtu_check_en = 1;
        dsl3edit4w.mtu_size = p_ol_tunnel->mtu_size & 0x3FFF;
    }
    if (CTC_FLAG_ISSET(p_ol_tunnel->flag, CTC_NH_OL_TUNNEL_FLAG_INT_EN))
    {
        dsl3edit4w.int_en = 1;
    }
    switch (p_ol_tunnel->tunnel_type)
    {
    case CTC_NH_OL_TUNNEL_TYPE_NVGRE:
        dsl3edit4w.ipsa_valid = 1;
        dsl3edit4w.inner_header_valid   = 1;
        dsl3edit4w.inner_header_type = 0x1;
        dsl3edit4w.gre_flags = 0x2;
        dsl3edit4w.ipda = p_ol_tunnel->ip_da.ipv4;
        dsl3edit4w.ipsa = p_ol_tunnel->ip_sa.ipv4;
        dsl3edit4w.share_type = SYS_NH_L3EDIT_TUNNELV4_SHARE_TYPE_NVGRE;
        dsl3edit4w.out_l2edit_valid = 1;
        dsl3edit4w.l2edit_ptr = sys_tunnel->l2edit_ptr;
        break;

    case CTC_NH_OL_TUNNEL_TYPE_GENEVE:  /*almost as vxlan*/
        dsl3edit4w.is_geneve = 1;
    case CTC_NH_OL_TUNNEL_TYPE_VXLAN:
        dsl3edit4w.inner_header_valid   = 1;
        dsl3edit4w.ipsa_valid = 1;
        dsl3edit4w.inner_header_type = 0x2; /* 2'b10: 8 byte UDP header */
        dsl3edit4w.ipda = p_ol_tunnel->ip_da.ipv4;
        dsl3edit4w.ipsa = p_ol_tunnel->ip_sa.ipv4;
        dsl3edit4w.share_type = SYS_NH_L3EDIT_TUNNELV4_SHARE_TYPE_VXLAN;
        dsl3edit4w.out_l2edit_valid = 1;
        dsl3edit4w.l2edit_ptr = sys_tunnel->l2edit_ptr;

        if (CTC_FLAG_ISSET(p_ol_tunnel->flag, CTC_NH_OL_TUNNEL_FLAG_VXLAN_GPE))
        {
            dsl3edit4w.is_evxlan = 1;
        }
        else if (CTC_FLAG_ISSET(p_ol_tunnel->flag, CTC_NH_OL_TUNNEL_FLAG_VXLAN_GBP))
        {
            /*using rsv profile 1 as GBP encap*/
            dsl3edit4w.is_geneve = 0;
            dsl3edit4w.evxlan_protocl_index = 1;
            dsl3edit4w.is_vxlan_gbp = 1;
        }

        if (CTC_FLAG_ISSET(p_ol_tunnel->flag,CTC_NH_OL_TUNNEL_FLAG_VXLAN_L4_SRC_PORT))
        {
            dsl3edit4w.udp_src_port_en = 1;
            dsl3edit4w.l4_src_port = p_ol_tunnel->l4_src_port;
        }
        break;

    default:
        return CTC_E_INVALID_PARAM;
    }

    if(p_ol_tunnel->tunnel_type == CTC_NH_OL_TUNNEL_TYPE_VXLAN || p_ol_tunnel->tunnel_type == CTC_NH_OL_TUNNEL_TYPE_GENEVE)
    {
        CTC_ERROR_RETURN(_sys_usw_nh_tunnel_build_vxlan_edit_profile(lchip, p_ol_tunnel, tunnel_db));
    }
    dsl3edit4w.vxlan_edit_idx = tunnel_db->vxlan_edit_idx;
    dsl3edit4w.xedit_ptr = sys_tunnel->xedit_ptr;
    CTC_ERROR_RETURN(sys_usw_nh_add_l3edit_tunnel(lchip, (void*)&dsl3edit4w,
            tunnel_db->is_4x? SYS_NH_ENTRY_TYPE_L3EDIT_TUNNEL_V4_4X: SYS_NH_ENTRY_TYPE_L3EDIT_TUNNEL_V4, &(sys_tunnel->l3edit_ptr)));
    tunnel_db->flag |= SYS_NH_OL_TUNNEL_FLAG_IN_V4;

    return ret;
}

STATIC int32
_sys_usw_nh_tunnel_build_dsl3edit_v6(uint8 lchip, ctc_nh_ol_tunnel_t* p_ol_tunnel,
                                              sys_nh_db_ol_tunnel_t* tunnel_db, sys_ldp_t* sys_tunnel)
{
    sys_dsl3edit_tunnelv6_t dsl3editv6;
    uint8 sa_index = MCHIP_CAP(SYS_CAP_NH_IP_TUNNEL_INVALID_IPSA_NUM);
	int32 ret = CTC_E_NONE; 
	uint8 old_index = tunnel_db->sa_index;

    sal_memset(&dsl3editv6, 0, sizeof(dsl3editv6));
    dsl3editv6.l3_rewrite_type = DRV_ENUM(DRV_L3EDITTYPE_ADDIP6);
    dsl3editv6.ds_type   = SYS_NH_DS_TYPE_L3EDIT;

    if (p_ol_tunnel->dot1ae_chan_id)
    {
        dsl3editv6.cloud_sec_en = 1;
        dsl3editv6.sc_index = tunnel_db->sc_index;
        dsl3editv6.sci_en = tunnel_db->sci_en;
    }

    /*ttl*/
    dsl3editv6.ttl = p_ol_tunnel->ttl;
    dsl3editv6.map_ttl = CTC_FLAG_ISSET(p_ol_tunnel->flag,
                                        CTC_NH_OL_TUNNEL_FLAG_MAP_TTL);
    if (p_ol_tunnel->ecn_select ==  CTC_NH_ECN_SELECT_NONE)
    {
        dsl3editv6.ecn_mode = 3;
    }
    else if (p_ol_tunnel->ecn_select ==  CTC_NH_ECN_SELECT_ASSIGN)
    {
        return CTC_E_NOT_SUPPORT;
    }
    else if (p_ol_tunnel->ecn_select ==  CTC_NH_ECN_SELECT_MAP)
    {
        dsl3editv6.ecn_mode = 1;
    }
    else if (p_ol_tunnel->ecn_select ==  CTC_NH_ECN_SELECT_PACKET)
    {
        dsl3editv6.ecn_mode = 0;
    }

    /*stats*/
    if (p_ol_tunnel->stats_id)
    {
        CTC_ERROR_RETURN(sys_usw_flow_stats_get_statsptr(lchip,
                                                           p_ol_tunnel->stats_id,
                                                           &(dsl3editv6.stats_ptr)));
    }

    if (p_ol_tunnel->dscp_select == CTC_NH_DSCP_SELECT_ASSIGN)
    {
        CTC_MAX_VALUE_CHECK(p_ol_tunnel->dscp_or_tos, SYS_NH_OVERLAY_MAX_DSCP_VALUE);
        dsl3editv6.derive_dscp = 1;
        dsl3editv6.tos = (p_ol_tunnel->dscp_or_tos<<2);
    }
    else if (p_ol_tunnel->dscp_select == CTC_NH_DSCP_SELECT_MAP)
    {
        dsl3editv6.derive_dscp = 2;
        dsl3editv6.tos = 1 << 0;
        dsl3editv6.dscp_domain = p_ol_tunnel->dscp_domain;
    }
    else if (p_ol_tunnel->dscp_select == CTC_NH_DSCP_SELECT_PACKET)
    {
        dsl3editv6.derive_dscp = 3;
        dsl3editv6.tos = 0 << 0;
    }
    else if (p_ol_tunnel->dscp_select == CTC_NH_DSCP_SELECT_NONE)
    {
        dsl3editv6.derive_dscp = 0;
    }
    else
    {
        return CTC_E_INVALID_PARAM;
    }

    if (p_ol_tunnel->flow_label_mode == CTC_NH_FLOW_LABEL_NONE)
    {
        dsl3editv6.new_flow_label_valid = 0x0;
    }
    else if (p_ol_tunnel->flow_label_mode == CTC_NH_FLOW_LABEL_WITH_HASH ||
             p_ol_tunnel->flow_label_mode == CTC_NH_FLOW_LABEL_ASSIGN)
    {
        if (p_ol_tunnel->flow_label > 0xFFFFF)
        {
            return CTC_E_INVALID_PARAM;
        }

        dsl3editv6.new_flow_label_valid = 0x1;
        dsl3editv6.new_flow_label_mode = (p_ol_tunnel->flow_label_mode == CTC_NH_FLOW_LABEL_WITH_HASH);
        dsl3editv6.flow_label = p_ol_tunnel->flow_label;
    }
    else
    {
        return CTC_E_INVALID_PARAM;
    }

    if (CTC_FLAG_ISSET(p_ol_tunnel->flag, CTC_NH_OL_TUNNEL_FLAG_MTU_CHECK))
    {
        if (p_ol_tunnel->mtu_size > 0x3FFF)
        {
            return CTC_E_INVALID_PARAM;
        }

        dsl3editv6.mtu_check_en = 1;
        dsl3editv6.mtu_size = p_ol_tunnel->mtu_size & 0x3FFF;
    }
    if (CTC_FLAG_ISSET(p_ol_tunnel->flag, CTC_NH_OL_TUNNEL_FLAG_INT_EN))
    {
        dsl3editv6.int_en = 1;
    }
    /* ipda, use little india for DRV_SET_FIELD_A */
    dsl3editv6.ipda[0] = p_ol_tunnel->ip_da.ipv6[3];
    dsl3editv6.ipda[1] = p_ol_tunnel->ip_da.ipv6[2];
    dsl3editv6.ipda[2] = p_ol_tunnel->ip_da.ipv6[1];
    dsl3editv6.ipda[3] = p_ol_tunnel->ip_da.ipv6[0];

    switch (p_ol_tunnel->tunnel_type)
    {
    case CTC_NH_OL_TUNNEL_TYPE_NVGRE:
        dsl3editv6.inner_header_valid   = 1;
        /* gre with key */
        dsl3editv6.inner_header_type = 0x1;
        dsl3editv6.gre_flags = 0x2;

        CTC_ERROR_RETURN(_sys_usw_nh_ip_tunnel_alloc_ipsa_idx(lchip, CTC_IP_VER_6,
                              &(p_ol_tunnel->ip_sa.ipv6), &sa_index, tunnel_db->sa_index));
        dsl3editv6.ipsa_index = sa_index & 0xF;
        dsl3editv6.share_type = SYS_NH_L3EDIT_TUNNELV6_SHARE_TYPE_NVGRE;
        dsl3editv6.out_l2edit_valid = 1;
        dsl3editv6.l2edit_ptr = sys_tunnel->l2edit_ptr;
        break;
    case CTC_NH_OL_TUNNEL_TYPE_GENEVE: /* almost as vxlan*/
        dsl3editv6.is_geneve = 1;
    case CTC_NH_OL_TUNNEL_TYPE_VXLAN:
        dsl3editv6.inner_header_valid   = 1;
        dsl3editv6.inner_header_type = 0x2; /* 2'b10: 8 byte UDP header */
        CTC_ERROR_RETURN(_sys_usw_nh_ip_tunnel_alloc_ipsa_idx(lchip, CTC_IP_VER_6,
                                  &(p_ol_tunnel->ip_sa.ipv6), &sa_index, tunnel_db->sa_index));

        dsl3editv6.ipsa_index = sa_index & 0xF;
        dsl3editv6.share_type = SYS_NH_L3EDIT_TUNNELV6_SHARE_TYPE_VXLAN;
        dsl3editv6.out_l2edit_valid = 1;
        dsl3editv6.l2edit_ptr = sys_tunnel->l2edit_ptr;


        if (CTC_FLAG_ISSET(p_ol_tunnel->flag,CTC_NH_OL_TUNNEL_FLAG_VXLAN_GPE))
        {
            dsl3editv6.is_evxlan = 1;
        }
        else if (CTC_FLAG_ISSET(p_ol_tunnel->flag, CTC_NH_OL_TUNNEL_FLAG_VXLAN_GBP))
        {
            /*using rsv profile 1 as GBP encap*/
            dsl3editv6.is_geneve = 0;
            dsl3editv6.evxlan_protocl_index = 1;
            dsl3editv6.is_vxlan_gbp = 1;
        }

        if (CTC_FLAG_ISSET(p_ol_tunnel->flag,CTC_NH_OL_TUNNEL_FLAG_VXLAN_L4_SRC_PORT))
        {
            dsl3editv6.udp_src_port_en = 1;
            dsl3editv6.l4_src_port = p_ol_tunnel->l4_src_port;
        }
        break;

    default:
        return CTC_E_INVALID_PARAM;
    }
    tunnel_db->sa_index = sa_index;

    if(sa_index != MCHIP_CAP(SYS_CAP_NH_IP_TUNNEL_INVALID_IPSA_NUM))
    {
          CTC_SET_FLAG(tunnel_db->flag,SYS_NH_OL_TUNNEL_HAVE_IPSA);
          tunnel_db->sa_index = sa_index;
    }

    if(p_ol_tunnel->tunnel_type == CTC_NH_OL_TUNNEL_TYPE_VXLAN || p_ol_tunnel->tunnel_type == CTC_NH_OL_TUNNEL_TYPE_GENEVE)
    {
        CTC_ERROR_GOTO(_sys_usw_nh_tunnel_build_vxlan_edit_profile(lchip, p_ol_tunnel, tunnel_db),ret,rollback2);
    }
    dsl3editv6.vxlan_edit_idx = tunnel_db->vxlan_edit_idx;
    dsl3editv6.xedit_ptr = sys_tunnel->xedit_ptr;
    CTC_ERROR_GOTO(sys_usw_nh_add_l3edit_tunnel(lchip, (void*)&dsl3editv6, SYS_NH_ENTRY_TYPE_L3EDIT_TUNNEL_V6, &(sys_tunnel->l3edit_ptr)), ret, rollback1);

    tunnel_db->dsl3edit_offset = sys_tunnel->l3edit_ptr;
    tunnel_db->flag |= SYS_NH_OL_TUNNEL_FLAG_IN_V6;
    return CTC_E_NONE;

rollback1:
    if (tunnel_db->vxlan_edit_idx)
    {
        sys_nh_vxlan_hw_profile_t profile;
        profile.profile_idx = tunnel_db->vxlan_edit_idx;
        sys_usw_nh_remove_vxlan_edit_profile(lchip, &profile);
        tunnel_db->vxlan_edit_idx = 0;
    }

rollback2:
    if (sa_index !=  MCHIP_CAP(SYS_CAP_NH_IP_TUNNEL_INVALID_IPSA_NUM))
    {
        _sys_usw_nh_ip_tunnel_free_ipsa_idx(lchip, CTC_IP_VER_6, sa_index);
        tunnel_db->sa_index = old_index;
    }
    return ret ;
}

extern sys_usw_nh_api_master_t* p_usw_nh_api_master[CTC_MAX_LOCAL_CHIP_NUM_PP];

STATIC int32
_sys_usw_nh_alloc_tunnel_resource(uint8 lchip, uint32 ldp,
                                                 ctc_nh_ol_tunnel_t* p_ol_tunnel,
                                                 sys_nh_db_ol_tunnel_t* tunnel_db,
                                                 sys_com_ldp_update_t* p_update)

{
    sys_nh_db_arp_t *p_arp = NULL;
    sys_nh_info_arp_param_t arp_parm;
    sys_ldp_t sys_ldp;
    uint16 lport = 0;
    uint8 gchip = 0;
    uint32 dsfwd_offset = 0;
    sys_nh_info_dsnh_t fp_info;

    if (DRV_FROM_AT(lchip) && p_ol_tunnel->dot1ae_chan_id)
    {
        return CTC_E_NOT_SUPPORT;
    }
    sal_memset(&sys_ldp, 0, sizeof(sys_ldp_t));
    sal_memset(&fp_info, 0, sizeof(fp_info));

    if(CTC_NH_OL_TUNNEL_TYPE_NONE == p_ol_tunnel->tunnel_type)
    {
        /*TO AC!!!!!!!!!!!!!!!!!!!!!!!*/
        /*Gport check!!!!!!!!!!*/
        tunnel_db->gport = p_ol_tunnel->oif.gport;
        p_update->destmap = SYS_ENCODE_DESTMAP(CTC_MAP_GPORT_TO_GCHIP(tunnel_db->gport), CTC_MAP_GPORT_TO_LPORT(tunnel_db->gport));

        if (DRV_FROM_AT(lchip))
        {
            sys_hbnh_brguc_node_info_t *p_brguc_nhid_info = NULL;
            sys_hbnh_brguc_node_info_t brguc_node_tmp;
            /*stats*/
            if (p_ol_tunnel->stats_id)
            {
                CTC_ERROR_RETURN(sys_usw_flow_stats_get_statsptr(lchip, p_ol_tunnel->stats_id, &sys_ldp.stats_ptr));
            }
            brguc_node_tmp.gport = p_ol_tunnel->oif.gport;
            p_brguc_nhid_info = ctc_hash_lookup(p_usw_nh_api_master[lchip]->brguc_info.brguc_hash, &brguc_node_tmp);
            if (NULL == p_brguc_nhid_info)
            {
                SYS_NH_DBG_OUT(CTC_DEBUG_LEVEL_INFO, " [Nexthop] Nexthop not exist \n");
                return CTC_E_NOT_EXIST;
            }
            if (SYS_NH_OVERLAY_NEW_MODE(lchip))
            {
                /*get dsfwd*/
                CTC_ERROR_RETURN(sys_usw_nh_get_dsfwd1_offset(lchip, p_brguc_nhid_info->nhid_brguc, &dsfwd_offset, 1, CTC_FEATURE_NEXTHOP));
                tunnel_db->ul_nhid = dsfwd_offset;
                CTC_SET_FLAG(tunnel_db->flag, SYS_NH_OL_TUNNEL_FLAG_DSFWD);
            }
        }
    }
    else
    {
        /*TO Tunnel!!!!!!!!!!!!!!!!!!!!!!!*/
        CTC_MAX_VALUE_CHECK(p_ol_tunnel->dscp_domain, 0xF);
        if (CTC_FLAG_ISSET(p_ol_tunnel->flag, CTC_NH_OL_TUNNEL_FLAG_VXLAN_GPE)
            && CTC_FLAG_ISSET(p_ol_tunnel->flag, CTC_NH_OL_TUNNEL_FLAG_VXLAN_GBP))
        {
            return CTC_E_INVALID_PARAM;
        }
        CTC_MAX_VALUE_CHECK(p_ol_tunnel->vxlan_rsv1, 0xFFFFFF);

        if (DRV_FROM_AT(lchip) && p_ol_tunnel->fp_id && MCHIP_NH(lchip)->get_fp_info)
        {
            CTC_ERROR_RETURN(MCHIP_NH(lchip)->get_fp_info(lchip, p_ol_tunnel->fp_id, &fp_info));
            if (0 == fp_info.fp2)
            {
                return CTC_E_INVALID_CONFIG;
            }
            sys_ldp.xedit_ptr = fp_info.l3_edit_ptr;
            tunnel_db->dot1ae_channel = p_ol_tunnel->fp_id;
        }

        if (CTC_FLAG_ISSET(p_ol_tunnel->flag, CTC_NH_OL_TUNNEL_FLAG_REROUTE))
        {
            if (p_ol_tunnel->dot1ae_chan_id)
            {
                SYS_NH_DBG_OUT(CTC_DEBUG_LEVEL_ERROR, " [Nexthop] Not support reroute with cloudsec!\n");
                return CTC_E_NOT_SUPPORT;
            }

            CTC_ERROR_RETURN(sys_usw_internal_port_get_rsv_port(lchip, SYS_INTERNAL_PORT_TYPE_IP_TUNNEL, 0, &lport));
            sys_usw_get_gchip_id(lchip, &gchip);
            tunnel_db->gport = SYS_MAP_DRV_LPORT_TO_CTC_GPORT(gchip, lport);
            CTC_SET_FLAG(tunnel_db->flag, SYS_NH_OL_TUNNEL_REROUTE);
            p_update->destmap = SYS_ENCODE_DESTMAP(gchip, lport);
            sys_ldp.dest_vlan_ptr = MCHIP_CAP(SYS_CAP_L3IF_RSV_L3IF_ID) + MCHIP_CAP(SYS_CAP_L3IF_VLAN_PTR_BASE);
            dsfwd_offset = g_usw_nh_master[lchip]->reroute_dsfwd_offset;
        }
        else if(CTC_FLAG_ISSET(p_ol_tunnel->flag, CTC_NH_OL_TUNNEL_FLAG_UNDERLAY_NH))
        {
            sys_nh_info_com_t *p_nhinfo = NULL;
            CTC_ERROR_DUMP(sys_usw_nh_get_nhinfo_by_nhid(lchip, p_ol_tunnel->nhid, &p_nhinfo));
            if (NULL == p_nhinfo)
            {
                SYS_NH_DBG_OUT(CTC_DEBUG_LEVEL_ERROR, " [Nexthop] underlay nhid not exist!\n");
                return CTC_E_NOT_EXIST;
            }
            if (!SYS_NH_OVERLAY_NEW_MODE(lchip))
            {
                SYS_NH_DBG_OUT(CTC_DEBUG_LEVEL_ERROR, " [Nexthop] only complete decoupling mode support !\n");
                return CTC_E_NOT_SUPPORT;
            }

            if(p_nhinfo->hdr.nh_entry_type == SYS_NH_TYPE_IPUC)
            {
                sys_ldp_t ldp_temp;
                sys_nh_info_ipuc_t *p_ipuc_db = NULL;
                sys_nh_info_dsnh_t  nh_info_dsnh;
                p_ipuc_db = (sys_nh_info_ipuc_t *)p_nhinfo;

                if (0 == p_ipuc_db->ldp_ext)
                {
                    return CTC_E_NOT_SUPPORT;
                }
                tunnel_db->arp_id = p_ipuc_db->arp_id;
                tunnel_db->gport = p_ipuc_db->gport;

                sal_memset(&ldp_temp, 0, sizeof(ldp_temp));
                CTC_ERROR_RETURN(sys_usw_nh_read_asic_table(lchip, SYS_NH_ENTRY_TYPE_LOGIC_DEST_PORT, p_ipuc_db->ldp_ext, &ldp_temp));
                sys_ldp.l2edit_ptr = ldp_temp.l2edit_ptr;
                sys_ldp.dest_vlan_ptr = ldp_temp.dest_vlan_ptr;

                /*get dsfwd*/
                CTC_ERROR_RETURN(sys_usw_nh_get_dsfwd1_offset(lchip, p_ol_tunnel->nhid, &dsfwd_offset, 1, CTC_FEATURE_NEXTHOP));
                CTC_SET_FLAG(tunnel_db->flag, SYS_NH_OL_TUNNEL_FLAG_DSFWD);
                sal_memset(&nh_info_dsnh, 0, sizeof(nh_info_dsnh));
                CTC_ERROR_RETURN(_sys_usw_nh_get_nhinfo(lchip,  p_ol_tunnel->nhid, &nh_info_dsnh));
                p_update->destmap = nh_info_dsnh.dest_map;
            }
            else if(p_nhinfo->hdr.nh_entry_type == SYS_NH_TYPE_ECMP)
            {
               /*get ecmp gid*/
               sys_nh_info_ecmp_t *p_ecmp_db = NULL;

               p_ecmp_db = (sys_nh_info_ecmp_t*) p_nhinfo;
               p_update->ecmp_gid = p_ecmp_db->ecmp_group_id;
               tunnel_db->gport = p_ecmp_db->gport;
            }
            else
            {
                return CTC_E_NOT_SUPPORT;
            }

            tunnel_db->ul_nhid = p_ol_tunnel->nhid;
            tunnel_db->ul_nh_en = 1;
        }
        else if(CTC_FLAG_ISSET(p_ol_tunnel->flag, CTC_NH_OL_TUNNEL_FLAG_ECMP_IF))
        {
            sys_l3if_ecmp_if_t ecmp_if = {0};
            CTC_ERROR_RETURN(sys_usw_l3if_get_ecmp_if_info(lchip, p_ol_tunnel->arp_id, &ecmp_if));
            tunnel_db->ecmp_if_id = p_ol_tunnel->arp_id;

            sys_ldp.l2edit_ptr = g_usw_nh_master[lchip]->ecmp_if_resolved_l2edit;
            sys_ldp.dest_vlan_ptr = MCHIP_CAP(SYS_CAP_L3IF_VLAN_PTR_BASE);

            p_update->ecmp_if_id = tunnel_db->ecmp_if_id;
            p_update->destmap = SYS_ENCODE_ECMP_DESTMAP(ecmp_if.hw_group_id);
            p_update->ecmp_gid = ecmp_if.hw_group_id;

        }
        else if(p_ol_tunnel->arp_id)
        {
            tunnel_db->ul_nh_en = 0;
            p_arp = (sys_usw_nh_lkup_arp_id(lchip, p_ol_tunnel->arp_id));
            if (NULL == p_arp)
            {
                return CTC_E_NOT_EXIST;
            }
            sal_memset(&arp_parm, 0, sizeof(sys_nh_info_arp_param_t));
            arp_parm.nh_entry_type  = SYS_NH_TYPE_OVERLAY_VNI;
            CTC_ERROR_RETURN(sys_usw_nh_bind_arp_cb(lchip, &arp_parm, p_ol_tunnel->arp_id));
            tunnel_db->arp_id = p_ol_tunnel->arp_id;
            /*used for build l3edit */
            sys_ldp.l2edit_ptr   = p_arp->ldp_mac_profile ? g_usw_nh_master[lchip]->ecmp_if_resolved_l2edit : p_arp->offset;
            if(p_arp->l3if_id)
            {
                sys_l3if_prop_t l3if_prop;
                sal_memset(&l3if_prop, 0, sizeof(sys_l3if_prop_t));
                l3if_prop.l3if_id = p_arp->l3if_id;
                CTC_ERROR_RETURN(sys_usw_l3if_get_l3if_info(lchip, 1, &l3if_prop));
                sys_ldp.dest_vlan_ptr = l3if_prop.vlan_ptr;
            }

            p_update->destmap = SYS_ENCODE_ARP_DESTMAP(p_arp->destmap_profile);

            /*
                compatible mode, not recommand !!!
            */
            if (DRV_FROM_AT(lchip) && SYS_NH_OVERLAY_NEW_MODE(lchip))
            {
                CTC_ERROR_RETURN(sys_usw_nh_arp_alloc_dsfwd(lchip, p_arp, &dsfwd_offset));
                CTC_SET_FLAG(tunnel_db->flag, SYS_NH_OL_TUNNEL_FLAG_DSFWD);
                tunnel_db->ul_nhid = dsfwd_offset;
            }
        }
        else
        {
            /*arp id == 0*/
            lport = SYS_RSV_PORT_DROP_ID;
            sys_usw_get_gchip_id(lchip, &gchip);
            tunnel_db->gport = SYS_MAP_DRV_LPORT_TO_CTC_GPORT(gchip, lport);
            p_update->destmap = SYS_ENCODE_DESTMAP(gchip, lport);

            /*refer to _sys_usw_nh_dsfwd_init_for_drop*/
            dsfwd_offset = 1;
        }
        if (0xFFFF != p_ol_tunnel->mtu_size)/*remote entry in egress edit, no need hw resource*/
        {
            CTC_ERROR_RETURN(_sys_usw_nh_tunnel_build_dsl3edit_prepare(lchip, p_ol_tunnel, tunnel_db, &sys_ldp));
            if (CTC_FLAG_ISSET(p_ol_tunnel->flag, CTC_NH_OL_TUNNEL_FLAG_IPV6))
            {
                CTC_ERROR_RETURN(_sys_usw_nh_tunnel_build_dsl3edit_v6(lchip, p_ol_tunnel, tunnel_db, &sys_ldp));
            }
            else
            {
                CTC_ERROR_RETURN(_sys_usw_nh_tunnel_build_dsl3edit_v4(lchip, p_ol_tunnel, tunnel_db, &sys_ldp));
            }
            tunnel_db->dsl3edit_offset = sys_ldp.l3edit_ptr;
        }
    }

    sys_ldp.lp_grp_id = p_ol_tunnel->lp_grp_id;
    if (CTC_FLAG_ISSET(p_ol_tunnel->flag, CTC_NH_OL_TUNNEL_FLAG_LP_GRP_EN))
    {
        sys_ldp.scl0_hash_type= DRV_ENUM(DRV_EGRESSSCLHASHTYPE_FIDDVPGROUP);
    }

    if (CTC_FLAG_ISSET(p_ol_tunnel->flag, CTC_NH_OL_TUNNEL_FLAG_ISOLATION_EN))
    {
        sys_ldp.isolation_id = p_ol_tunnel->isolation_id;
        sys_ldp.scl1_hash_type = (0xFFFF == p_ol_tunnel->isolation_id)?
                                  DRV_ENUM(DRV_EGRESSSCLHASHTYPE_VPPAIR)
                                : DRV_ENUM(DRV_EGRESSSCLHASHTYPE_METADATADVPGROUP);
    }

    /*used for Write DsLogicDestPort*/
    sys_ldp.l2edit_ptr   =  p_arp ? p_arp->ldp_mac_profile : 0;

    if (CTC_FLAG_ISSET(p_ol_tunnel->flag, CTC_NH_OL_TUNNEL_FLAG_NSH_EN))
    {
        sys_ldp.nsh_en = 1;
        CTC_SET_FLAG(tunnel_db->flag, SYS_NH_OL_TUNNEL_FLAG_NSH_EN);
        if (CTC_NH_OL_TUNNEL_TYPE_NONE == p_ol_tunnel->tunnel_type)
        {
            sys_ldp.l3edit_ptr = g_usw_nh_master[lchip]->rsv_l3edit_for_nsh;
        }
    }

    CTC_ERROR_RETURN(sys_usw_nh_write_asic_table(lchip, SYS_NH_ENTRY_TYPE_LOGIC_DEST_PORT, ldp, &sys_ldp));

    /*write DsIngressLogicPort*/
    if (DRV_FROM_AT(lchip) && SYS_NH_OVERLAY_NEW_MODE(lchip))
    {
        uint32 cmd = 0;
        DsIngressLogicDestPort_m  ds_logic_port;
        sal_memset(&ds_logic_port, 0, sizeof(ds_logic_port));

        if (p_update->ecmp_gid != 0)
        {
            SetDsIngressLogicDestPort(V, isEcmp_f, &ds_logic_port, 1);
            SetDsIngressLogicDestPort(V, u_gEcmp_ecmpGroupId_f, &ds_logic_port, p_update->ecmp_gid);
        }
        else
        {
            SetDsIngressLogicDestPort(V, u_gFwd_dsFwdPtr_f, &ds_logic_port, dsfwd_offset);
        }

        cmd = DRV_IOW(DsIngressLogicDestPort_t, DRV_ENTRY_FLAG);
        CTC_ERROR_RETURN(DRV_IOCTL(lchip, ldp , cmd, &ds_logic_port));
    }

    return CTC_E_NONE;
}

STATIC int32
_sys_usw_nh_free_tunnel_resource(uint8 lchip, sys_nh_db_ol_tunnel_t* tunnel_db, uint8 is_update)
{
    uint8 l3edit_type = 0;

    if(CTC_NH_OL_TUNNEL_TYPE_NONE == tunnel_db->tunnel_type)
    {
         return CTC_E_NONE;
    }

    if (!CTC_FLAG_ISSET(tunnel_db->flag, SYS_NH_OL_TUNNEL_REROUTE))
    {
    }


    if ( CTC_FLAG_ISSET(tunnel_db->flag, SYS_NH_OL_TUNNEL_HAVE_IPSA))
    {
        if (CTC_FLAG_ISSET(tunnel_db->flag, SYS_NH_OL_TUNNEL_FLAG_IN_V6))
        {
            _sys_usw_nh_ip_tunnel_free_ipsa_idx(lchip, CTC_IP_VER_6, tunnel_db->sa_index);
        }
        else
        {
            _sys_usw_nh_ip_tunnel_free_ipsa_idx(lchip, CTC_IP_VER_4, tunnel_db->sa_index);
        }
        CTC_UNSET_FLAG(tunnel_db->flag, SYS_NH_OL_TUNNEL_HAVE_IPSA);
    }

    if (tunnel_db->dsl3edit_offset)
    {
        if (CTC_FLAG_ISSET(tunnel_db->flag, SYS_NH_OL_TUNNEL_FLAG_IN_V4))
        {
            l3edit_type = tunnel_db->is_4x? SYS_NH_ENTRY_TYPE_L3EDIT_TUNNEL_V4_4X: SYS_NH_ENTRY_TYPE_L3EDIT_TUNNEL_V4;
        }
        else if (CTC_FLAG_ISSET(tunnel_db->flag, SYS_NH_OL_TUNNEL_FLAG_IN_V6))
        {
            l3edit_type = SYS_NH_ENTRY_TYPE_L3EDIT_TUNNEL_V6;
        }

        if(tunnel_db->vxlan_edit_idx)
        {
            sys_nh_vxlan_hw_profile_t profile;
            profile.profile_idx = tunnel_db->vxlan_edit_idx;
            sys_usw_nh_remove_vxlan_edit_profile(lchip, &profile);
            tunnel_db->vxlan_edit_idx = 0;
        }
        sys_usw_nh_remove_l3edit_tunnel(lchip, l3edit_type, tunnel_db->dsl3edit_offset);
        tunnel_db->dsl3edit_offset = 0;
    }

    if (tunnel_db->arp_id && tunnel_db->ul_nh_en == 0)
    {
        sys_usw_nh_unbind_arp_cb(lchip, tunnel_db->arp_id, 0, NULL);
        tunnel_db->arp_id = 0;
    }

    if ((tunnel_db->dot1ae_en) && (!is_update))
    {
        sys_com_dot1ae_bind_sc_t bind_sc;
        sal_memset(&bind_sc, 0, sizeof(bind_sc));
        bind_sc.type = 1;
        bind_sc.dir = 0;
        bind_sc.gport = tunnel_db->logic_port;

        bind_sc.sc_index = (tunnel_db->sc_index >> (DRV_FROM_TMM(lchip)? 0 : 2));
        CTC_ERROR_RETURN(SYS_CB(SYS_CB_DOT1AE_UNBIND_SEC_CHAN, lchip,&bind_sc));
        tunnel_db->dot1ae_en = 0;
    }

    CTC_UNSET_FLAG(tunnel_db->flag, SYS_NH_OL_TUNNEL_FLAG_IN_V4);
    CTC_UNSET_FLAG(tunnel_db->flag, SYS_NH_OL_TUNNEL_FLAG_IN_V6);
    return CTC_E_NONE;
}

STATIC int32
_sys_usw_nh_tunnel_get_dsl3edit_v4(uint8 lchip, ctc_nh_ol_tunnel_t* p_ol_tunnel,
                                              sys_nh_db_ol_tunnel_t* tunnel_db, sys_ldp_t* sys_tunnel)
{
    ds0_t ds;
    sys_usw_nh_master_t* p_master = g_usw_nh_master[lchip];
    uint8 tunnel_type;
    sys_dsl3edit_tunnelv4_t dsl3edit4w;

    sal_memset(&dsl3edit4w, 0, sizeof(dsl3edit4w));
    tunnel_type = tunnel_db->is_4x ? SYS_NH_ENTRY_TYPE_L3EDIT_TUNNEL_V4_4X : SYS_NH_ENTRY_TYPE_L3EDIT_TUNNEL_V4;
    CTC_ERROR_RETURN(sys_usw_nh_get_asic_table(lchip, tunnel_type, tunnel_db->dsl3edit_offset, &ds));

    dsl3edit4w.share_type = (tunnel_db->tunnel_type == CTC_NH_OL_TUNNEL_TYPE_NVGRE) ? \
        SYS_NH_L3EDIT_TUNNELV4_SHARE_TYPE_NVGRE : SYS_NH_L3EDIT_TUNNELV4_SHARE_TYPE_VXLAN;

    if(p_master->nh_table_info_array[tunnel_type].p_func_unmap)
    {
        p_master->nh_table_info_array[tunnel_type].p_func_unmap(lchip, &dsl3edit4w, &ds);
    }

    p_ol_tunnel->ttl = dsl3edit4w.ttl;
    if(dsl3edit4w.map_ttl)
    {
        CTC_SET_FLAG(p_ol_tunnel->flag, CTC_NH_OL_TUNNEL_FLAG_MAP_TTL);
    }

    p_ol_tunnel->ecn_select = dsl3edit4w.ecn_mode==0 ? CTC_NH_ECN_SELECT_PACKET : \
        dsl3edit4w.ecn_mode == 1 ?  CTC_NH_ECN_SELECT_MAP : CTC_NH_ECN_SELECT_NONE;

    if(dsl3edit4w.stats_ptr)
    {
        CTC_ERROR_RETURN(sys_usw_flow_stats_lookup_statsid(lchip, CTC_STATS_STATSID_TYPE_TUNNEL, dsl3edit4w.stats_ptr, \
            &p_ol_tunnel->stats_id, CTC_EGRESS));
    }

    (dsl3edit4w.copy_dont_frag) ? CTC_SET_FLAG(p_ol_tunnel->flag, CTC_NH_OL_TUNNEL_FLAG_COPY_DONT_FRAG) : 0;
    (dsl3edit4w.dont_frag) ?  CTC_SET_FLAG(p_ol_tunnel->flag, CTC_NH_OL_TUNNEL_FLAG_DONT_FRAG) : 0;

    if(0 == dsl3edit4w.derive_dscp)
    {
        p_ol_tunnel->dscp_select = CTC_NH_DSCP_SELECT_NONE;
    }
    else if(3 == dsl3edit4w.derive_dscp)
    {
        p_ol_tunnel->dscp_select = CTC_NH_DSCP_SELECT_PACKET;
    }
    else if(2 == dsl3edit4w.derive_dscp)
    {
        p_ol_tunnel->dscp_select = CTC_NH_DSCP_SELECT_MAP;
        p_ol_tunnel->dscp_domain = dsl3edit4w.dscp_domain;
    }
    else if(1 == dsl3edit4w.derive_dscp)
    {
        p_ol_tunnel->dscp_select = CTC_NH_DSCP_SELECT_ASSIGN;
        p_ol_tunnel->dscp_or_tos = dsl3edit4w.dscp;
    }


    if(1 == dsl3edit4w.mtu_check_en)
    {
        CTC_SET_FLAG(p_ol_tunnel->flag, CTC_NH_OL_TUNNEL_FLAG_MTU_CHECK);
        p_ol_tunnel->mtu_size = dsl3edit4w.mtu_size;
    }

    dsl3edit4w.int_en ? CTC_SET_FLAG(p_ol_tunnel->flag, CTC_NH_OL_TUNNEL_FLAG_INT_EN) : 0;

    p_ol_tunnel->ip_da.ipv4 = dsl3edit4w.ipda;
    p_ol_tunnel->ip_sa.ipv4 = dsl3edit4w.ipsa;
    /*ipsa index*/
    if(CTC_FLAG_ISSET(tunnel_db->flag, SYS_NH_OL_TUNNEL_HAVE_IPSA))
    {
        DsL3TunnelV4IpSa_m v4_sa;
        uint32 cmd = DRV_IOR(DsL3TunnelV4IpSa_t, DRV_ENTRY_FLAG);
        CTC_ERROR_RETURN(DRV_IOCTL(lchip, tunnel_db->sa_index, cmd, &v4_sa));
        p_ol_tunnel->ip_sa.ipv4 = GetDsL3TunnelV4IpSa(V, ipSa_f, &v4_sa);
    }

    if(dsl3edit4w.udp_src_port_en)
    {
        p_ol_tunnel->l4_src_port = dsl3edit4w.l4_src_port;
        CTC_SET_FLAG(p_ol_tunnel->flag, CTC_NH_OL_TUNNEL_FLAG_VXLAN_L4_SRC_PORT);
    }
    return CTC_E_NONE;
}

STATIC int32
_sys_usw_nh_tunnel_get_dsl3edit_v6(uint8 lchip, ctc_nh_ol_tunnel_t* p_ol_tunnel,
                                              sys_nh_db_ol_tunnel_t* tunnel_db, sys_ldp_t* sys_tunnel)
{
    ds0_t ds;
    sys_usw_nh_master_t* p_master = g_usw_nh_master[lchip];
    sys_dsl3edit_tunnelv6_t dsl3editv6;

    sal_memset(&dsl3editv6, 0, sizeof(dsl3editv6));
    CTC_ERROR_RETURN(sys_usw_nh_get_asic_table(lchip, SYS_NH_ENTRY_TYPE_L3EDIT_TUNNEL_V6, tunnel_db->dsl3edit_offset, &ds));
    dsl3editv6.share_type = (tunnel_db->tunnel_type == CTC_NH_OL_TUNNEL_TYPE_NVGRE) ? \
        SYS_NH_L3EDIT_TUNNELV6_SHARE_TYPE_NVGRE : SYS_NH_L3EDIT_TUNNELV6_SHARE_TYPE_VXLAN;
    if(p_master->nh_table_info_array[SYS_NH_ENTRY_TYPE_L3EDIT_TUNNEL_V6].p_func_unmap)
    {
        p_master->nh_table_info_array[SYS_NH_ENTRY_TYPE_L3EDIT_TUNNEL_V6].p_func_unmap(lchip, &dsl3editv6, &ds);
    }

    p_ol_tunnel->ttl = dsl3editv6.ttl;
    if(dsl3editv6.map_ttl)
    {
        CTC_SET_FLAG(p_ol_tunnel->flag, CTC_NH_OL_TUNNEL_FLAG_MAP_TTL);
    }

    p_ol_tunnel->ecn_select = dsl3editv6.ecn_mode==0 ? CTC_NH_ECN_SELECT_PACKET : \
        dsl3editv6.ecn_mode == 1 ?  CTC_NH_ECN_SELECT_MAP : CTC_NH_ECN_SELECT_NONE;

    if(dsl3editv6.stats_ptr)
    {
        sys_usw_flow_stats_lookup_statsid(lchip, CTC_STATS_STATSID_TYPE_TUNNEL, dsl3editv6.stats_ptr, \
            &p_ol_tunnel->stats_id, CTC_EGRESS);
    }

    if(0 == dsl3editv6.derive_dscp)
    {
        p_ol_tunnel->dscp_select = CTC_NH_DSCP_SELECT_NONE;
    }
    else if(3 == dsl3editv6.derive_dscp)
    {
        p_ol_tunnel->dscp_select = CTC_NH_DSCP_SELECT_PACKET;
    }
    else if(2 == dsl3editv6.derive_dscp)
    {
        p_ol_tunnel->dscp_select = CTC_NH_DSCP_SELECT_MAP;
        p_ol_tunnel->dscp_domain = dsl3editv6.dscp_domain;
    }
    else if(1 == dsl3editv6.derive_dscp)
    {
        p_ol_tunnel->dscp_select = CTC_NH_DSCP_SELECT_ASSIGN;
        p_ol_tunnel->dscp_or_tos = dsl3editv6.tos>>2;
    }

    p_ol_tunnel->flow_label_mode = CTC_NH_FLOW_LABEL_NONE;
    if(dsl3editv6.new_flow_label_valid)
    {
        p_ol_tunnel->flow_label_mode = dsl3editv6.new_flow_label_mode ? CTC_NH_FLOW_LABEL_WITH_HASH : CTC_NH_FLOW_LABEL_ASSIGN;
        p_ol_tunnel->flow_label = dsl3editv6.flow_label;
    }
    if(1 == dsl3editv6.mtu_check_en)
    {
        CTC_SET_FLAG(p_ol_tunnel->flag, CTC_NH_OL_TUNNEL_FLAG_MTU_CHECK);
        p_ol_tunnel->mtu_size = dsl3editv6.mtu_size;
    }

    dsl3editv6.int_en ? CTC_SET_FLAG(p_ol_tunnel->flag, CTC_NH_OL_TUNNEL_FLAG_INT_EN) : 0;

    p_ol_tunnel->ip_da.ipv6[3] = dsl3editv6.ipda[0];
    p_ol_tunnel->ip_da.ipv6[2] = dsl3editv6.ipda[1];
    p_ol_tunnel->ip_da.ipv6[1] = dsl3editv6.ipda[2];
    p_ol_tunnel->ip_da.ipv6[0] = dsl3editv6.ipda[3];
    /*ipsa index*/
    if(CTC_FLAG_ISSET(tunnel_db->flag, SYS_NH_OL_TUNNEL_HAVE_IPSA))
    {
        uint32 cmd;
        DsL3TunnelV6IpSa_m v6_sa;
        ipv6_addr_t hw_ipv6_address;
        cmd = DRV_IOR(DsL3TunnelV6IpSa_t, DRV_ENTRY_FLAG);
        CTC_ERROR_RETURN(DRV_IOCTL(lchip, tunnel_db->sa_index, cmd, &v6_sa));
        GetDsL3TunnelV6IpSa(A, ipSa_f, &v6_sa, hw_ipv6_address);
        p_ol_tunnel->ip_sa.ipv6[3] = hw_ipv6_address[0];
        p_ol_tunnel->ip_sa.ipv6[2] = hw_ipv6_address[1];
        p_ol_tunnel->ip_sa.ipv6[1] = hw_ipv6_address[2];
        p_ol_tunnel->ip_sa.ipv6[0] = hw_ipv6_address[3];
    }

    if(dsl3editv6.udp_src_port_en)
    {
        p_ol_tunnel->l4_src_port = dsl3editv6.l4_src_port;
        CTC_SET_FLAG(p_ol_tunnel->flag, CTC_NH_OL_TUNNEL_FLAG_VXLAN_L4_SRC_PORT);
    }
    return CTC_E_NONE;
}

int32
_sys_usw_nh_get_ol_tunnel(uint8 lchip, uint32 ldp, ctc_nh_ol_tunnel_t* p_ol_tunnel)
{
    sys_ldp_t sys_ldp;
    sys_nh_db_ol_tunnel_t* tunnel_db = NULL;

    SYS_NH_DBG_OUT(CTC_DEBUG_LEVEL_FUNC, "\n%s()\n", __FUNCTION__);
    CTC_MAX_VALUE_CHECK(p_ol_tunnel->lp_grp_id, SYS_LP_GRP_ID_MAX);
    sal_memset(&sys_ldp, 0, sizeof(sys_ldp));
    tunnel_db = _sys_usw_nh_lkup_tunnel(lchip, ldp);
    if (NULL == tunnel_db)
    {
        SYS_NH_DBG_OUT(CTC_DEBUG_LEVEL_ERROR, " [Nexthop] overlay tunnel not exist!\n");
        return CTC_E_NOT_EXIST;
    }

    p_ol_tunnel->tunnel_type = tunnel_db->tunnel_type;
    if (tunnel_db->ul_nh_en)
    {
        p_ol_tunnel->nhid = tunnel_db->ul_nhid;
		CTC_SET_FLAG(p_ol_tunnel->flag, CTC_NH_OL_TUNNEL_FLAG_UNDERLAY_NH);
    }
    /*out interface*/
    if(CTC_NH_OL_TUNNEL_TYPE_NONE == p_ol_tunnel->tunnel_type)
    {
        p_ol_tunnel->oif.gport = tunnel_db->gport;
    }
    else if(CTC_FLAG_ISSET(tunnel_db->flag, SYS_NH_OL_TUNNEL_REROUTE))
    {
        CTC_SET_FLAG(p_ol_tunnel->flag, CTC_NH_OL_TUNNEL_FLAG_REROUTE);
    }
    else if(tunnel_db->ecmp_if_id)
    {
        CTC_SET_FLAG(p_ol_tunnel->flag, CTC_NH_OL_TUNNEL_FLAG_ECMP_IF);
        p_ol_tunnel->arp_id = tunnel_db->ecmp_if_id;
    }
    else
    {
        p_ol_tunnel->arp_id = tunnel_db->arp_id;
    }
    if(CTC_NH_OL_TUNNEL_TYPE_NONE != p_ol_tunnel->tunnel_type)
    {
        /*dot1ae*/
        if (DRV_FROM_AT(lchip))
        {
            p_ol_tunnel->fp_id = tunnel_db->dot1ae_channel;
        }
        else if(tunnel_db->dot1ae_en)
        {
            p_ol_tunnel->dot1ae_chan_id = tunnel_db->dot1ae_channel;
        }

        /*tunnel info*/
        if(CTC_FLAG_ISSET(tunnel_db->flag, SYS_NH_OL_TUNNEL_FLAG_IN_V6))
        {
            CTC_ERROR_RETURN(_sys_usw_nh_tunnel_get_dsl3edit_v6(lchip, p_ol_tunnel, tunnel_db, &sys_ldp));
            CTC_SET_FLAG(p_ol_tunnel->flag, CTC_NH_OL_TUNNEL_FLAG_IPV6);
        }
        else
        {
            CTC_ERROR_RETURN(_sys_usw_nh_tunnel_get_dsl3edit_v4(lchip, p_ol_tunnel, tunnel_db, &sys_ldp));

        }
        /*vxlan rsv field*/
        if(tunnel_db->vxlan_edit_idx && p_ol_tunnel->tunnel_type == CTC_NH_OL_TUNNEL_TYPE_VXLAN)
        {
            sys_nh_vxlan_hw_profile_t* p_vxlan_profile = &g_usw_nh_master[lchip]->vxlan_edit_profile[tunnel_db->vxlan_edit_idx];

            if(p_vxlan_profile->rsv3_as_protocol && p_vxlan_profile->flags == 0xC)
            {
                CTC_SET_FLAG(p_ol_tunnel->flag, CTC_NH_OL_TUNNEL_FLAG_VXLAN_GPE);
				p_ol_tunnel->vxlan_flags = p_vxlan_profile->flags;
            }
            else
            {
                if(p_vxlan_profile->with_policy_group)
                {
                    CTC_SET_FLAG(p_ol_tunnel->flag, CTC_NH_OL_TUNNEL_FLAG_VXLAN_GBP);
                }
                p_ol_tunnel->vxlan_flags = p_vxlan_profile->flags;
                p_ol_tunnel->vxlan_rsv1 = p_vxlan_profile->rsv1<<16|p_vxlan_profile->rsv2<<8|p_vxlan_profile->rsv3;
                p_ol_tunnel->vxlan_rsv2 = p_vxlan_profile->rsv7;
            }
        }
    }
    /*isolation id and lp grp id*/
    CTC_ERROR_RETURN(sys_usw_nh_read_asic_table(lchip, SYS_NH_ENTRY_TYPE_LOGIC_DEST_PORT, ldp, &sys_ldp));

    if(sys_ldp.scl0_hash_type == DRV_ENUM(DRV_EGRESSSCLHASHTYPE_FIDDVPGROUP))
    {
        CTC_SET_FLAG(p_ol_tunnel->flag, CTC_NH_OL_TUNNEL_FLAG_LP_GRP_EN);
        p_ol_tunnel->lp_grp_id = sys_ldp.lp_grp_id;
    }

    if((sys_ldp.scl1_hash_type == DRV_ENUM(DRV_EGRESSSCLHASHTYPE_VPPAIR)) ||
         (sys_ldp.scl1_hash_type ==DRV_ENUM(DRV_EGRESSSCLHASHTYPE_METADATADVPGROUP)))
    {
        CTC_SET_FLAG(p_ol_tunnel->flag, CTC_NH_OL_TUNNEL_FLAG_ISOLATION_EN);
        p_ol_tunnel->isolation_id = sys_ldp.isolation_id;
    }

    if (sys_ldp.nsh_en)
    {
        CTC_SET_FLAG(p_ol_tunnel->flag, CTC_NH_OL_TUNNEL_FLAG_NSH_EN);
    }

    if(sys_ldp.stats_ptr)
    {
        sys_usw_flow_stats_lookup_statsid(lchip, CTC_STATS_STATSID_TYPE_TUNNEL, sys_ldp.stats_ptr, \
            &p_ol_tunnel->stats_id, CTC_EGRESS);
    }

    return CTC_E_NONE;
}

int32
_sys_usw_nh_update_ol_tunnel_ref_info(uint8 lchip, sys_nh_info_com_t* p_nhinfo, sys_nh_db_ol_tunnel_t* tunnel_db, uint8 is_add)
{
    sys_nh_ref_list_node_t* p_curr = NULL;
    sys_nh_ref_list_node_t* p_nh_ref_list_node = NULL;
    sys_nh_ref_list_node_t* p_prev = NULL;
    uint8 find_flag = 0;

    p_curr = tunnel_db->p_ref_nh_list;

    if (is_add)
    {
        while (p_curr)
        {
            if (p_curr->p_ref_nhinfo == p_nhinfo)
            {
                find_flag = 1;
                break;
            }

            p_curr = p_curr->p_next;
        }

        if (find_flag == 0)
        {
            p_nh_ref_list_node = (sys_nh_ref_list_node_t*)mem_malloc(MEM_NEXTHOP_MODULE, sizeof(sys_nh_ref_list_node_t));
            if (p_nh_ref_list_node == NULL)
            {
                SYS_NH_DBG_OUT(CTC_DEBUG_LEVEL_ERROR, " No memory \n");
                return CTC_E_NO_MEMORY;
            }
            p_nh_ref_list_node->p_ref_nhinfo = (sys_nh_info_com_t* )p_nhinfo;
            p_nh_ref_list_node->p_next = tunnel_db->p_ref_nh_list;

            tunnel_db->p_ref_nh_list = p_nh_ref_list_node;
        }
    }
    else
    {
        while (p_curr)
        {
            if (p_curr->p_ref_nhinfo == p_nhinfo)
            {
                if (NULL == p_prev)
                /*Remove first node*/
                {
                   tunnel_db->p_ref_nh_list = p_curr->p_next;
                }
                else
                {
                    p_prev->p_next = p_curr->p_next;
                }

                mem_free(p_curr);
                break;
            }

            p_prev = p_curr;
            p_curr = p_curr->p_next;
        }
    }

    return CTC_E_NONE;
}

int32
sys_usw_nh_add_ol_tunnel(uint8 lchip, uint32 ldp, ctc_nh_ol_tunnel_t* p_ol_tunnel)
{
    sys_nh_db_ol_tunnel_t* tunnel_db = NULL;
    sys_usw_nh_master_t* p_master = g_usw_nh_master[lchip];
    int32 ret = CTC_E_NONE;
    sys_com_ldp_update_t update_param = {0};

    CTC_VALUE_RANGE_CHECK(ldp, 1, MCHIP_CAP(SYS_CAP_NH_MAX_LOGIC_DEST_PORT));
    CTC_MAX_VALUE_CHECK(p_ol_tunnel->lp_grp_id, SYS_LP_GRP_ID_MAX);
    if (!SYS_ISOLATION_ID_VALID(p_ol_tunnel->isolation_id))
    {
        return CTC_E_INVALID_PARAM;
    }

    SYS_NH_DBG_OUT(CTC_DEBUG_LEVEL_PARAM, "ldp = %u\n", ldp);

    tunnel_db = _sys_usw_nh_lkup_tunnel(lchip, ldp);
    if (tunnel_db)
    {
        SYS_NH_DBG_OUT(CTC_DEBUG_LEVEL_ERROR, " [Nexthop] overlay tunnel exist!\n");
        return CTC_E_EXIST;
    }

    #ifdef WARMBOOT
    if(CTC_WB_ENABLE(lchip) && p_master->ol_tunnel_hash
        && p_master->ol_tunnel_hash->count >= SYS_FTM_SPEC(lchip, CTC_FTM_SPEC_TUNNEL))
    {
        return CTC_E_NO_RESOURCE;
    }
    #endif
    tunnel_db  = (sys_nh_db_ol_tunnel_t*)mem_malloc(MEM_NEXTHOP_MODULE, sizeof(sys_nh_db_ol_tunnel_t));
    if (NULL == tunnel_db)
    {
        SYS_NH_DBG_OUT(CTC_DEBUG_LEVEL_ERROR, " [Nexthop] No memory \n");
        return CTC_E_NO_MEMORY;
    }
    sal_memset(tunnel_db, 0, sizeof(sys_nh_db_ol_tunnel_t));
    tunnel_db->logic_port = ldp;
    tunnel_db->tunnel_type = p_ol_tunnel->tunnel_type;
    tunnel_db->sa_index = MCHIP_CAP(SYS_CAP_NH_IP_TUNNEL_INVALID_IPSA_NUM);

    CTC_ERROR_GOTO(_sys_usw_nh_alloc_tunnel_resource(lchip, ldp, p_ol_tunnel, tunnel_db, &update_param), ret, error_proc);
    if (NULL == ctc_hash_insert(p_master->ol_tunnel_hash, tunnel_db))
    {
        SYS_NH_DBG_OUT(CTC_DEBUG_LEVEL_ERROR, " [Nexthop] No memory \n");
        ret = CTC_E_NO_MEMORY;
        goto error_proc;
    }

    /*Update DsEcmp, init list*/

    SYS_USW_REGISTER_WB_SYNC_EN(lchip, CTC_FEATURE_NEXTHOP, SYS_WB_APPID_NH_SUBID_OL_TUNNEL, 1);
    return CTC_E_NONE;
error_proc:
    _sys_usw_nh_free_tunnel_resource(lchip, tunnel_db, 0);
    mem_free(tunnel_db);

    return ret;
}
int32
sys_usw_nh_remove_ol_tunnel(uint8 lchip, uint32 ldp)
{
    sys_nh_db_ol_tunnel_t* tunnel_db = NULL;
    sys_ldp_t sys_ldp;

    tunnel_db = _sys_usw_nh_lkup_tunnel(lchip, ldp);
    if (NULL == tunnel_db)
    {
        SYS_NH_DBG_OUT(CTC_DEBUG_LEVEL_ERROR, " [Nexthop] overlay tunnel not exist!\n");
        return CTC_E_NOT_EXIST;
    }

    /*ldp tunnel is used by other overlay nhid*/
    if (NULL != tunnel_db->p_ref_nh_list)
    {
        return CTC_E_IN_USE;
    }

    _sys_usw_nh_free_tunnel_resource(lchip, tunnel_db, 0);
    sal_memset(&sys_ldp, 0, sizeof(sys_ldp));
    CTC_ERROR_DUMP(sys_usw_nh_write_asic_table(lchip, SYS_NH_ENTRY_TYPE_LOGIC_DEST_PORT, ldp, &sys_ldp));

    /*clear DsIngressLogicPort*/
    if (DRV_FROM_AT(lchip))
    {
        uint32 cmd = 0;
        DsIngressLogicDestPort_m  ds_logic_port;
        sal_memset(&ds_logic_port, 0, sizeof(ds_logic_port));
        cmd = DRV_IOW(DsIngressLogicDestPort_t, DRV_ENTRY_FLAG);
        CTC_ERROR_RETURN(DRV_IOCTL(lchip, ldp , cmd, &ds_logic_port));
    }

    ctc_hash_remove(g_usw_nh_master[lchip]->ol_tunnel_hash, tunnel_db);
    mem_free(tunnel_db);
    SYS_USW_REGISTER_WB_SYNC_EN(lchip, CTC_FEATURE_NEXTHOP, SYS_WB_APPID_NH_SUBID_OL_TUNNEL, 1);
    return CTC_E_NONE;
}
int32
sys_usw_nh_update_ol_tunnel(uint8 lchip, uint32 ldp, ctc_nh_ol_tunnel_t* p_ol_tunnel, sys_com_ldp_update_t* update_param)
{
    sys_nh_db_ol_tunnel_t* tunnel_db = NULL;
    sys_nh_db_ol_tunnel_t tunnel_db_old;
    int32 ret = CTC_E_NONE;
    uint8 update = 0;
    sys_nh_info_dsnh_t dsnh;

    sal_memset(&dsnh, 0, sizeof(sys_nh_info_dsnh_t));
    CTC_MAX_VALUE_CHECK(p_ol_tunnel->lp_grp_id, SYS_LP_GRP_ID_MAX);
    if (!SYS_ISOLATION_ID_VALID(p_ol_tunnel->isolation_id))
    {
        return CTC_E_INVALID_PARAM;
    }
    tunnel_db = _sys_usw_nh_lkup_tunnel(lchip, ldp);
    if (NULL == tunnel_db)
    {
        SYS_NH_DBG_OUT(CTC_DEBUG_LEVEL_ERROR, " [Nexthop] overlay tunnel not exist!\n");
        return CTC_E_NOT_EXIST;
    }

    if ((p_ol_tunnel->tunnel_type != tunnel_db->tunnel_type) && (!DRV_FROM_AT(lchip) || !SYS_NH_OVERLAY_NEW_MODE(lchip)))
    {
        SYS_NH_DBG_OUT(CTC_DEBUG_LEVEL_ERROR, " [Nexthop] Tunnel type change not support!\n");
        return CTC_E_NOT_SUPPORT;
    }

    /*Judge Update condition*/
    if ( (p_ol_tunnel->tunnel_type != CTC_NH_OL_TUNNEL_TYPE_NONE) &&
        ((CTC_FLAG_ISSET(p_ol_tunnel->flag, CTC_NH_OL_TUNNEL_FLAG_REROUTE)
            != CTC_FLAG_ISSET(tunnel_db->flag, SYS_NH_OL_TUNNEL_REROUTE))
        ||(CTC_FLAG_ISSET(p_ol_tunnel->flag, CTC_NH_OL_TUNNEL_FLAG_ECMP_IF)
            != (tunnel_db->ecmp_if_id?1:0) )
        || (CTC_FLAG_ISSET(p_ol_tunnel->flag, CTC_NH_OL_TUNNEL_FLAG_ECMP_IF) && (p_ol_tunnel->arp_id != tunnel_db->ecmp_if_id))
        || (!CTC_FLAG_ISSET(p_ol_tunnel->flag, CTC_NH_OL_TUNNEL_FLAG_ECMP_IF) && (p_ol_tunnel->arp_id != tunnel_db->arp_id))
        || (CTC_FLAG_ISSET(p_ol_tunnel->flag, CTC_NH_OL_TUNNEL_FLAG_UNDERLAY_NH) && (!tunnel_db->ul_nh_en || (p_ol_tunnel->nhid != tunnel_db->ul_nhid)))
        || (!CTC_FLAG_ISSET(p_ol_tunnel->flag, CTC_NH_OL_TUNNEL_FLAG_UNDERLAY_NH) && tunnel_db->ul_nh_en)))
    {
         update = 1;
    }
    else if (p_ol_tunnel->tunnel_type == CTC_NH_OL_TUNNEL_TYPE_NONE &&
        p_ol_tunnel->oif.gport != tunnel_db->gport)
    {
        update = 1;
    }

    sal_memcpy(&tunnel_db_old, tunnel_db, sizeof(sys_nh_db_ol_tunnel_t));
    sal_memset(tunnel_db, 0, sizeof(sys_nh_db_ol_tunnel_t));
    tunnel_db->logic_port = ldp;
    tunnel_db->tunnel_type = p_ol_tunnel->tunnel_type;
    if (!DRV_FROM_AT(lchip))
    {
        tunnel_db->dot1ae_channel = tunnel_db_old.dot1ae_channel;
    }
    tunnel_db->dot1ae_en = tunnel_db_old.dot1ae_en;
    tunnel_db->sci_en = tunnel_db_old.sci_en;
    tunnel_db->sc_index = tunnel_db_old.sc_index;
    tunnel_db->sa_index = CTC_FLAG_ISSET(tunnel_db_old.flag, SYS_NH_OL_TUNNEL_HAVE_IPSA)? tunnel_db_old.sa_index:0xFF;
    tunnel_db->is_4x = tunnel_db_old.is_4x;
    tunnel_db->p_ref_nh_list = tunnel_db_old.p_ref_nh_list;

    CTC_ERROR_GOTO(_sys_usw_nh_alloc_tunnel_resource(lchip, ldp, p_ol_tunnel, tunnel_db, update_param), ret, error_proc);
    _sys_usw_nh_free_tunnel_resource(lchip, &tunnel_db_old, 1);

    /*Update underlay*/
    if (DRV_FROM_AT(lchip) && tunnel_db->p_ref_nh_list && SYS_NH_OVERLAY_NEW_MODE(lchip))
    {
        sys_nh_ref_list_node_t* p_curr = NULL;
        sys_nh_info_overlay_t* p_ol_nh_db = NULL;
        
        p_curr = tunnel_db->p_ref_nh_list;
        while (p_curr)
        {
            p_ol_nh_db = (sys_nh_info_overlay_t*)p_curr->p_ref_nhinfo;
            if (tunnel_db->ul_nh_en == 0 && p_ol_tunnel->arp_id)
            {
                p_ol_nh_db->hdr.dsfwd1_offset = tunnel_db->ul_nhid;
            }
            p_curr = p_curr->p_next;
        }
    }
    if (update)
    {
        update_param->ldp = ldp;
        update_param->ul_nh_en = tunnel_db->ul_nh_en;
        /*Update Overlay*/
        sys_usw_nh_update_overaly(lchip, update_param, tunnel_db);
        update_param->is_update = 1;
    }

    SYS_USW_REGISTER_WB_SYNC_EN(lchip, CTC_FEATURE_NEXTHOP, SYS_WB_APPID_NH_SUBID_OL_TUNNEL, 1);


    return CTC_E_NONE;
error_proc:
     _sys_usw_nh_free_tunnel_resource(lchip, tunnel_db, 1);
    if (tunnel_db_old.dot1ae_en && !tunnel_db->dot1ae_en)
    {
        sys_com_dot1ae_bind_sc_t bind_sc;
        sal_memset(&bind_sc,0,sizeof(bind_sc));
        bind_sc.type = 1;
        bind_sc.dir = 0;  /*tx*/
        bind_sc.gport = tunnel_db_old.logic_port;
        bind_sc.chan_id = tunnel_db_old.dot1ae_channel;
        SYS_CB(SYS_CB_DOT1AE_BIND_SEC_CHAN, lchip,&bind_sc);
    }
    sal_memcpy(tunnel_db, &tunnel_db_old, sizeof(sys_nh_db_ol_tunnel_t));
    return ret;
}

#define OVERLAY_VNI

STATIC int32
_sys_usw_nh_overlay_build_dsl2edit_inner(uint8 lchip, sys_nh_param_ol_vni_t* p_nh_param,
                                           sys_nh_info_ol_vni_t* p_edit_db,
                                           sys_nh_param_dsnh_t* dsnh)
{
    sys_dsl2edit_eth_t   dsl2edit;
    mac_addr_t mac = {0};
    ctc_nh_ol_vni_t* p_overlay_param = p_nh_param->p_ol_vni_param;

    sal_memset(&dsl2edit, 0 , sizeof(dsl2edit));
    if (CTC_FLAG_ISSET(p_overlay_param->flag,CTC_NH_OL_VNI_FLAG_CROSS_VNI))
    {
        sal_memcpy(dsl2edit.mac_da, p_overlay_param->macda, sizeof(mac_addr_t));
        dsl2edit.derive_mcast_mac_for_ip = 1;
        if (CTC_FLAG_ISSET(p_overlay_param->flag, CTC_NH_OL_VNI_FLAG_UPDATE_MACSA))
        {
            dsl2edit.update_mac_sa = 1;
        }
        dsl2edit.ether_type = p_nh_param->p_ol_vni_param->inner_ether_type;

        if (0 == sal_memcmp(p_overlay_param->macsa, mac, 6))
        {
            CTC_ERROR_RETURN(sys_usw_nh_add_l2edit(lchip, &dsl2edit, SYS_NH_ENTRY_TYPE_L2EDIT_INNER_3W_RW + SYS_NH_TYPE_STEP_TO_INNER, &dsnh->inner_l2edit_ptr));
            CTC_SET_FLAG(p_edit_db->hdr.nh_entry_flags, SYS_NH_INFO_FLAG_HAVE_L2EDI_3W);
        }
        else
        {
            sal_memcpy(dsl2edit.mac_sa, p_overlay_param->macsa, sizeof(mac_addr_t));
            CTC_ERROR_RETURN(sys_usw_nh_add_l2edit(lchip, &dsl2edit, SYS_NH_ENTRY_TYPE_L2EDIT_INNER_6W_RW + SYS_NH_TYPE_STEP_TO_INNER, &dsnh->inner_l2edit_ptr));
            CTC_SET_FLAG(p_edit_db->hdr.nh_entry_flags, SYS_NH_INFO_FLAG_HAVE_L2EDI_6W);
        }
    }
    return CTC_E_NONE;
}


STATIC int32
_sys_usw_nh_free_overlay_resource(uint8 lchip, sys_nh_info_ol_vni_t* p_nhinfo)
{
    if (CTC_FLAG_ISSET(p_nhinfo->hdr.nh_entry_flags, SYS_NH_INFO_FLAG_NO_HW))
    {
        return CTC_E_NONE;
    }

    if (CTC_FLAG_ISSET(p_nhinfo->hdr.nh_entry_flags, SYS_NH_INFO_FLAG_HAVE_L2EDI_6W))
    {
        sys_usw_nh_remove_l2edit(lchip, SYS_NH_ENTRY_TYPE_L2EDIT_INNER_6W_RW + SYS_NH_TYPE_STEP_TO_INNER, p_nhinfo->inner_l2_edit_offset);
        CTC_UNSET_FLAG(p_nhinfo->hdr.nh_entry_flags, SYS_NH_INFO_FLAG_HAVE_L2EDI_6W);
    }
    else if (CTC_FLAG_ISSET(p_nhinfo->hdr.nh_entry_flags, SYS_NH_INFO_FLAG_HAVE_L2EDI_3W))
    {
        sys_usw_nh_remove_l2edit(lchip, SYS_NH_ENTRY_TYPE_L2EDIT_INNER_3W_RW + SYS_NH_TYPE_STEP_TO_INNER, p_nhinfo->inner_l2_edit_offset);
        CTC_UNSET_FLAG(p_nhinfo->hdr.nh_entry_flags, SYS_NH_INFO_FLAG_HAVE_L2EDI_3W);
    }

    CTC_UNSET_FLAG(p_nhinfo->hdr.nh_entry_flags, SYS_NH_INFO_FLAG_HAVE_L3EDIT);
    CTC_UNSET_FLAG(p_nhinfo->hdr.nh_entry_flags, SYS_NH_INFO_FLAG_HAVE_L2EDIT);
    return CTC_E_NONE;
}



STATIC int32
_sys_usw_nh_overlay_build_dsnh(uint8 lchip, sys_nh_param_ol_vni_t* p_nh_param,
                                       sys_nh_info_ol_vni_t* p_edit_db)
{
    sys_nh_param_dsnh_t dsnh_param;
    int32 ret = CTC_E_NONE;
    ctc_nh_ol_vni_t* p_ol_vni_param;

    sal_memset(&dsnh_param, 0, sizeof(sys_nh_param_dsnh_t));
    p_ol_vni_param = p_nh_param->p_ol_vni_param;

    dsnh_param.dsnh_type = SYS_NH_PARAM_DSNH_TYPE_OVERLAY_VNI;
    if (CTC_FLAG_ISSET(p_ol_vni_param->flag, CTC_NH_OL_VNI_FLAG_HOVERLAY))
    {
        CTC_UNSET_FLAG(p_edit_db->hdr.nh_entry_flags, SYS_NH_INFO_FLAG_HORIZON_SPLIT_EN);
        dsnh_param.hvpls = 1;/*use for tunnel split horizon*/
    }
    else
    {
        CTC_SET_FLAG(p_edit_db->hdr.nh_entry_flags, SYS_NH_INFO_FLAG_HORIZON_SPLIT_EN);
        dsnh_param.hvpls = 0;/*use for tunnel split horizon*/
    }
    if (CTC_FLAG_ISSET(p_ol_vni_param->flag, CTC_NH_OL_VNI_FLAG_L3_OVERLAY))
    {
        dsnh_param.strip_l2_hdr = 1;
    }

    if (CTC_FLAG_ISSET(p_ol_vni_param->flag, CTC_NH_OL_VNI_FLAG_CROSS_VNI))
    {
        if (CTC_FLAG_ISSET(p_ol_vni_param->flag, CTC_NH_OL_VNI_FLAG_TTL_NO_DEC))
        {
            CTC_SET_FLAG(dsnh_param.flag, SYS_NH_PARAM_FLAG_ROUTE_NOTTL);
        }
    }
    else
    {
        CTC_SET_FLAG(dsnh_param.flag, SYS_NH_PARAM_FLAG_IP_TUNNEL_PLD_BRIDGE);
    }
    if (CTC_FLAG_ISSET(p_ol_vni_param->flag, CTC_NH_OL_VNI_FLAG_STRIP_VLAN))
    {
        CTC_SET_FLAG(dsnh_param.flag, SYS_NH_PARAM_FLAG_IP_TUNNEL_STRIP_VLAN);
    }
    if (CTC_FLAG_ISSET(p_ol_vni_param->flag, CTC_NH_OL_VNI_FLAG_STRIP_CVLAN))
    {
        CTC_SET_FLAG(dsnh_param.flag, SYS_NH_PARAM_FLAG_IP_TUNNEL_STRIP_CVLAN);
    }

    if(CTC_FLAG_ISSET(p_ol_vni_param->flag, CTC_NH_OL_VNI_FLAG_LOGIC_PORT))
    {
        CTC_SET_FLAG(p_edit_db->hdr.nh_entry_flags, SYS_NH_INFO_FLAG_LOGIC_PORT);
        dsnh_param.logic_port = p_edit_db->ldp;
    }

    if(CTC_FLAG_ISSET(p_ol_vni_param->flag, CTC_NH_OL_VNI_FLAG_VLAN_EDIT))
    {
        dsnh_param.p_vlan_info = &p_ol_vni_param->vlan_info;
        p_edit_db->svlan_edit_type = dsnh_param.p_vlan_info->svlan_edit_type;
        p_edit_db->cvlan_edit_type = dsnh_param.p_vlan_info->cvlan_edit_type;

        if (p_ol_vni_param->vlan_info.user_vlanptr == 0xffff)
        {
            dsnh_param.dest_vlan_ptr = (DRV_IS_DUET2(lchip))?0x1800:0x1000;
        }
    }
    /*stats*/
    if (CTC_FLAG_ISSET(p_ol_vni_param->vlan_info.flag, CTC_VLAN_NH_STATS_EN))
    {
        uint32 stats_ptr = 0;
        ret = sys_usw_flow_stats_get_statsptr(lchip,  p_ol_vni_param->vlan_info.stats_id, &stats_ptr);
        if (ret)
        {
            goto error_proc;
        }

        dsnh_param.stats_ptr = stats_ptr;
    }

    dsnh_param.logic_port_check = CTC_FLAG_ISSET(p_ol_vni_param->flag, CTC_NH_OL_VNI_FLAG_LOGIC_PORT_CHECK)? 1 : 0;
    dsnh_param.cid = p_ol_vni_param->cid;

    if (CTC_FLAG_ISSET(p_edit_db->hdr.nh_entry_flags, SYS_NH_INFO_FLAG_NO_HW))
    {
        return CTC_E_NONE;
    }

    ret = _sys_usw_nh_overlay_build_dsl2edit_inner(lchip, p_nh_param, p_edit_db, &dsnh_param);
    if (ret)
    {
        goto error_proc;
    }
    p_edit_db->inner_l2_edit_offset = dsnh_param.inner_l2edit_ptr;
    dsnh_param.dsnh_offset = p_edit_db->hdr.dsnh_offset;
    dsnh_param.dvp_tbl_valid = 1;
    if (CTC_FLAG_ISSET(p_ol_vni_param->flag, CTC_NH_OL_VNI_FLAG_FID))
    {
        dsnh_param.fid = p_ol_vni_param->fid;
    }
    if (CTC_FLAG_ISSET(p_edit_db->hdr.nh_entry_flags, SYS_NH_INFO_FLAG_USE_DSNH8W))
    {
        ret = sys_usw_nh_write_entry_dsnh8w(lchip, &dsnh_param);
        if (ret)
        {
            goto error_proc;
        }
    }
    else
    {
        ret = sys_usw_nh_write_entry_dsnh4w(lchip, &dsnh_param);
        if (ret)
        {
            goto error_proc;
        }
    }

   return CTC_E_NONE;
error_proc:
     return ret;
}
int32
sys_usw_nh_create_ol_vni_cb(uint8 lchip, sys_nh_param_com_t* p_com_nh_para, sys_nh_info_com_t* p_com_db)
{
    sys_nh_param_ol_vni_t* p_nh_param = NULL;
    sys_nh_info_ol_vni_t* p_nhdb = NULL;
    int32 ret = 0;

    p_nh_param = (sys_nh_param_ol_vni_t*)(p_com_nh_para);
    if (CTC_FLAG_ISSET(p_nh_param->p_ol_vni_param->flag, CTC_NH_OL_VNI_FLAG_FID))
    {
        if (p_nh_param->p_ol_vni_param->fid > MCHIP_CAP(SYS_CAP_SPEC_MAX_FID))
        {
            SYS_NH_DBG_OUT(CTC_DEBUG_LEVEL_ERROR, " FID is out of range!\n");
            return CTC_E_BADID;
        }
    }
    p_nhdb     = (sys_nh_info_ol_vni_t*)(p_com_db);
    if(CTC_FLAG_ISSET(p_nhdb->hdr.nh_entry_flags, SYS_NH_INFO_FLAG_USE_DSNH8W) && (p_nhdb->hdr.dsnh_offset & 0x01))
    {
        SYS_NH_DBG_OUT(CTC_DEBUG_LEVEL_ERROR, "  Nexthop offset is error, should use even number!\n");
        return CTC_E_BADID;
    }
    if (((p_nh_param->p_ol_vni_param->cid && !g_usw_nh_master[lchip]->cid_use_4w)
        || (CTC_FLAG_ISSET(p_nh_param->p_ol_vni_param->vlan_info.flag, CTC_VLAN_NH_STATS_EN)  && p_nh_param->p_ol_vni_param->cid))
        && (!CTC_FLAG_ISSET(p_nhdb->hdr.nh_entry_flags, SYS_NH_INFO_FLAG_USE_DSNH8W) ))
    {
        return CTC_E_INVALID_CONFIG;
    }
    p_nhdb->hdr.nh_entry_type = SYS_NH_TYPE_OVERLAY_VNI;
    CTC_ERROR_GOTO(_sys_usw_nh_overlay_build_dsnh(lchip, p_nh_param, p_nhdb), ret, error_proc);
    SYS_USW_REGISTER_WB_SYNC_EN(lchip, CTC_FEATURE_NEXTHOP, SYS_WB_APPID_NH_SUBID_INFO_COM, 1);
    return CTC_E_NONE;
error_proc:
    _sys_usw_nh_free_overlay_resource(lchip, p_nhdb);
    return ret;
}

int32
sys_usw_nh_delete_ol_vni_cb(uint8 lchip, sys_nh_info_com_t* p_data, sys_nh_param_com_t* p_com_nh_para)
{
    sys_nh_info_ol_vni_t* p_nhinfo   = NULL;
    p_nhinfo = (sys_nh_info_ol_vni_t*)(p_data);
    _sys_usw_nh_free_overlay_resource(lchip, p_nhinfo);
    SYS_USW_REGISTER_WB_SYNC_EN(lchip, CTC_FEATURE_NEXTHOP, SYS_WB_APPID_NH_SUBID_INFO_COM, 1);
    return CTC_E_NONE;
}

int32
sys_usw_nh_update_ol_vni_cb(uint8 lchip, sys_nh_info_com_t* p_nh_db,
                                     sys_nh_param_com_t* p_com_nh_para)
{
    sys_nh_info_ol_vni_t old_nh_info;
    sys_nh_param_ol_vni_t* p_nh_param = NULL;
    int32 ret = CTC_E_NONE;
    uint8 is_8w = 0;
    CTC_EQUAL_CHECK(SYS_NH_TYPE_OVERLAY_VNI, p_nh_db->hdr.nh_entry_type);
    sal_memcpy(&old_nh_info, p_nh_db, sizeof(sys_nh_info_ol_vni_t));
    sal_memset(p_nh_db, 0, sizeof(sys_nh_info_ol_vni_t));

    ((sys_nh_info_ol_vni_t*)p_nh_db)->ldp = old_nh_info.ldp;
    sys_usw_nh_copy_nh_entry_flags(lchip, &old_nh_info.hdr, &p_nh_db->hdr);

    p_nh_param = (sys_nh_param_ol_vni_t*)(p_com_nh_para);
    if (CTC_FLAG_ISSET(p_nh_param->p_ol_vni_param->flag, CTC_NH_OL_VNI_FLAG_FID)
        || CTC_FLAG_ISSET(p_nh_param->p_ol_vni_param->flag, CTC_NH_OL_VNI_FLAG_STRIP_CVLAN)
        || CTC_FLAG_ISSET(p_nh_param->p_ol_vni_param->flag, CTC_NH_OL_VNI_FLAG_CROSS_VNI)
        || (CTC_FLAG_ISSET(p_nh_param->p_ol_vni_param->flag, CTC_NH_OL_VNI_FLAG_VLAN_EDIT)
                     && ((p_nh_param->p_ol_vni_param->vlan_info.cvlan_edit_type != CTC_VLAN_EGRESS_EDIT_NONE
                           && p_nh_param->p_ol_vni_param->vlan_info.cvlan_edit_type != CTC_VLAN_EGRESS_EDIT_KEEP_VLAN_UNCHANGE)))
        || (CTC_FLAG_ISSET(p_nh_param->p_ol_vni_param->vlan_info.flag, CTC_VLAN_NH_STATS_EN) && g_usw_nh_master[lchip]->cid_use_4w))
    {
        is_8w = 1;
    }

    if ((0 == CTC_FLAG_ISSET(p_nh_db->hdr.nh_entry_flags, SYS_NH_INFO_FLAG_USE_DSNH8W)) && is_8w)
    {
        SYS_NH_DBG_OUT(CTC_DEBUG_LEVEL_ERROR, " [Nexthop] Should use DsNexthop8W table \n");
        ret = CTC_E_INVALID_CONFIG;
		goto error_proc;
    }

    if (CTC_FLAG_ISSET(p_nh_param->p_ol_vni_param->flag, CTC_NH_OL_VNI_FLAG_LOGIC_PORT) !=
        CTC_FLAG_ISSET(old_nh_info.hdr.nh_entry_flags, SYS_NH_INFO_FLAG_LOGIC_PORT) )
    {
        SYS_NH_DBG_OUT(CTC_DEBUG_LEVEL_ERROR, " [Nexthop] Not support logicPort flag change \n");
        ret = CTC_E_INVALID_CONFIG;
		goto error_proc;
    }


    CTC_ERROR_GOTO(sys_usw_nh_create_ol_vni_cb(lchip, p_com_nh_para,
                                                (sys_nh_info_com_t*)p_nh_db), ret, error_proc);
   _sys_usw_nh_free_overlay_resource(lchip, &old_nh_info);
   SYS_USW_REGISTER_WB_SYNC_EN(lchip, CTC_FEATURE_NEXTHOP, SYS_WB_APPID_NH_SUBID_INFO_COM, 1);
   return CTC_E_NONE;
error_proc:
    sal_memcpy(p_nh_db, &old_nh_info, sizeof(sys_nh_info_ol_vni_t));
    return ret;
}
int32
_sys_usw_nh_ol_decode_dsl2edit_inner(uint8 lchip, uint32 offset, sys_nh_info_ol_vni_t* p_nh_info, ctc_nh_ol_vni_t* p_ol_vni)
{
    uint8 table_type;
    sys_dsl2edit_eth_t ds_l2edit;

    sal_memset(&ds_l2edit, 0, sizeof(ds_l2edit));
    table_type = CTC_FLAG_ISSET(p_nh_info->hdr.nh_entry_flags, SYS_NH_INFO_FLAG_HAVE_L2EDI_6W) ? \
        SYS_NH_ENTRY_TYPE_L2EDIT_INNER_6W_RW : SYS_NH_ENTRY_TYPE_L2EDIT_INNER_3W_RW;
    CTC_ERROR_RETURN(sys_usw_nh_read_asic_table(lchip, table_type+SYS_NH_TYPE_STEP_TO_INNER, offset, &ds_l2edit));

    sal_memcpy(p_ol_vni->macda, ds_l2edit.mac_da, sizeof(mac_addr_t));
    sal_memcpy(p_ol_vni->macsa, ds_l2edit.mac_sa, sizeof(mac_addr_t));
    p_ol_vni->inner_ether_type = ds_l2edit.ether_type;
    if(ds_l2edit.update_mac_sa)
    {
        CTC_SET_FLAG(p_ol_vni->flag, CTC_NH_OL_VNI_FLAG_UPDATE_MACSA);
    }

    if (p_nh_info->hdr.nh_entry_flags & CTC_NH_OL_VNI_FLAG_VLAN_EDIT)
    {
        uint32 cmd = 0;
        DsNextHop8W_m ds_nh;
        ctc_vlan_egress_edit_info_t* p_vlan_info;
        cmd = DRV_IOR(DsNextHop8W_t, DRV_ENTRY_FLAG);
        CTC_ERROR_DUMP(DRV_IOCTL(lchip, p_nh_info->hdr.dsnh_offset, cmd, &ds_nh));
        p_vlan_info = &p_ol_vni->vlan_info;
        p_vlan_info->svlan_edit_type = p_nh_info->svlan_edit_type;
        p_vlan_info->cvlan_edit_type = p_nh_info->cvlan_edit_type;
        _sys_usw_nh_decode_vlan_edit(lchip, p_vlan_info, &ds_nh);
    }

    return CTC_E_NONE;
}

int32
sys_usw_nh_get_ol_vni(uint8 lchip, sys_nh_info_com_t* p_info, void* p_ol_vni_param)
{
    DsNextHop8W_m ds_nexthop;
    sys_nh_info_ol_vni_t* p_nh_info = (sys_nh_info_ol_vni_t*)p_info;
    ctc_nh_ol_vni_t* p_ol_vni = (ctc_nh_ol_vni_t*)p_ol_vni_param;
    uint8 table_type;
    sys_nexthop_t dsnh;

    CTC_PTR_VALID_CHECK(p_nh_info);
    CTC_PTR_VALID_CHECK(p_ol_vni);

    if(!CTC_FLAG_ISSET(p_nh_info->hdr.nh_entry_flags, SYS_NH_INFO_FLAG_HORIZON_SPLIT_EN))
    {
        CTC_SET_FLAG(p_ol_vni->flag, CTC_NH_OL_VNI_FLAG_HOVERLAY);
    }

    if(CTC_FLAG_ISSET(p_nh_info->hdr.nh_entry_flags, SYS_NH_INFO_FLAG_LOGIC_PORT))
    {
        CTC_SET_FLAG(p_ol_vni->flag, CTC_NH_OL_VNI_FLAG_LOGIC_PORT);
    }

    table_type = CTC_FLAG_ISSET(p_nh_info->hdr.nh_entry_flags, SYS_NH_INFO_FLAG_USE_DSNH8W) ? SYS_NH_ENTRY_TYPE_NEXTHOP_8W : SYS_NH_ENTRY_TYPE_NEXTHOP_4W;
    CTC_ERROR_RETURN(sys_usw_nh_get_asic_table(lchip, table_type, p_nh_info->hdr.dsnh_offset, &ds_nexthop));

    if(!GetDsNextHop4W(V, svlanTagged_f, &ds_nexthop) && GetDsNextHop4W(V, vlanXlateMode_f, &ds_nexthop))
    {
        CTC_SET_FLAG(p_ol_vni->flag, CTC_NH_OL_VNI_FLAG_STRIP_VLAN);
    }

    if(CTC_FLAG_ISSET(p_nh_info->hdr.nh_entry_flags, SYS_NH_INFO_FLAG_USE_DSNH8W))
    {
        if(GetDsNextHop8W(V, logicPortCheck_f,    &ds_nexthop))
        {
            CTC_SET_FLAG(p_ol_vni->flag, CTC_NH_OL_VNI_FLAG_LOGIC_PORT_CHECK);
        }
        if((0 == GetDsNextHop8W(V, u2Type_f, &ds_nexthop)) && GetDsNextHop8W(V, fidValid_f, &ds_nexthop))
        {
            CTC_SET_FLAG(p_ol_vni->flag, CTC_NH_OL_VNI_FLAG_FID);
            p_ol_vni->fid = GetDsNextHop8W(V, fid_f, &ds_nexthop);
        }

        if(!GetDsNextHop8W(V, cvlanTagged_f, &ds_nexthop) && GetDsNextHop8W(V, vlanXlateMode_f, &ds_nexthop))
        {
            CTC_SET_FLAG(p_ol_vni->flag,  CTC_NH_OL_VNI_FLAG_STRIP_CVLAN);
        }
        if(CTC_FLAG_ISSET(p_nh_info->hdr.nh_entry_flags, SYS_NH_INFO_FLAG_HAVE_L2EDI_3W) ||
                    CTC_FLAG_ISSET(p_nh_info->hdr.nh_entry_flags, SYS_NH_INFO_FLAG_HAVE_L2EDI_6W))
        {
            CTC_SET_FLAG(p_ol_vni->flag, CTC_NH_OL_VNI_FLAG_CROSS_VNI);
            CTC_ERROR_RETURN(_sys_usw_nh_ol_decode_dsl2edit_inner(lchip, GetDsNextHop8W(V, innerEditPtr_f, &ds_nexthop), p_nh_info, p_ol_vni));
            if(GetDsNextHop8W(V, payloadOperation_f, &ds_nexthop) == SYS_NH_OP_ROUTE_NOTTL)
            {
                CTC_SET_FLAG(p_ol_vni->flag, CTC_NH_OL_VNI_FLAG_TTL_NO_DEC);
            }
        }

    }
    else
    {
        if(GetDsNextHop4W(V, u1_g4_outerEditLocation_f,    &ds_nexthop))
        {
            CTC_SET_FLAG(p_ol_vni->flag, CTC_NH_OL_VNI_FLAG_LOGIC_PORT_CHECK);
        }
    }
    sal_memset(&dsnh, 0, sizeof(dsnh));
    CTC_ERROR_RETURN(sys_usw_nh_read_asic_table(lchip, CTC_FLAG_ISSET(p_nh_info->hdr.nh_entry_flags, SYS_NH_INFO_FLAG_USE_DSNH8W)?
    SYS_NH_ENTRY_TYPE_NEXTHOP_8W : SYS_NH_ENTRY_TYPE_NEXTHOP_4W, p_nh_info->hdr.dsnh_offset, &dsnh));
    if (dsnh.strip_l2_hdr)
    {
        CTC_SET_FLAG(p_ol_vni->flag, CTC_NH_OL_VNI_FLAG_L3_OVERLAY);
    }
    if (dsnh.cid_valid)
    {
        p_ol_vni->cid = dsnh.cid;
    }
    if (dsnh.stats_ptr)
    {
        sys_usw_flow_stats_lookup_statsid(lchip, CTC_STATS_STATSID_TYPE_NEXTHOP, dsnh.stats_ptr, &p_ol_vni->vlan_info.stats_id, CTC_EGRESS);
        CTC_SET_FLAG(p_ol_vni->vlan_info.flag, CTC_VLAN_NH_STATS_EN);
    }

    return CTC_E_NONE;
}
#define OVERLAY_NH
STATIC int32
_sys_usw_nh_update_overlay_ldp(uint8 lchip, sys_nh_info_overlay_t* p_nhdb,
                             sys_nh_param_overlay_t* p_para)
{
    sys_nh_info_ol_vni_t* p_ol_nhdb = NULL;
    sys_nh_info_ecmp_t *p_ecmp_nh = NULL;
    sys_nh_db_ol_tunnel_t* tunnel_db = NULL;
    sys_nh_db_ol_tunnel_t* old_tunnel_db = NULL;
    sys_com_ldp_update_t* update_param = p_para->p_ldp_update;
    ctc_nh_overlay_param_t* p_nh_param = p_para->p_overlay_nh_param;
    sys_l3if_ecmp_if_t ecmp_if = {0};

    SYS_NH_DBG_OUT(CTC_DEBUG_LEVEL_FUNC, "\n%s()\n", __FUNCTION__);

    CTC_EQUAL_CHECK(SYS_NH_TYPE_OVERLAY, p_nhdb->hdr.nh_entry_type);

    if(p_nhdb->nhid != p_nh_param->ol_vni)
    {
        SYS_NH_DBG_OUT(CTC_DEBUG_LEVEL_ERROR, "Not support update overlay vni or overlay vni is 0\n");
        return CTC_E_NOT_SUPPORT;
    }

    if ((p_nh_param->ldp == p_nhdb->dest_logic_port) && (CTC_FLAG_ISSET(p_nh_param->flag, CTC_NH_OL_FLAG_LDP_ECMP) == p_nhdb->is_ecmp_nh))
    {
        return CTC_E_NONE;
    }

    if (p_nh_param->ol_vni)
    {
        CTC_ERROR_DUMP(sys_usw_nh_get_nhinfo_by_nhid(lchip, p_nh_param->ol_vni, (sys_nh_info_com_t**)&p_ol_nhdb));
        if ((NULL == p_ol_nhdb) || (SYS_NH_TYPE_OVERLAY_VNI != p_ol_nhdb->hdr.nh_entry_type))
        {
            SYS_NH_DBG_OUT(CTC_DEBUG_LEVEL_ERROR, " [Nexthop] overlay vni not exist!\n");
            return CTC_E_NOT_EXIST;
        }
        update_param->bind_ldp = CTC_FLAG_ISSET(p_ol_nhdb->hdr.nh_entry_flags, SYS_NH_INFO_FLAG_LOGIC_PORT);
    }

    /*0. Check and get ecmp if info*/
    if (!CTC_FLAG_ISSET(p_nh_param->flag, CTC_NH_OL_FLAG_LDP_ECMP))
    {
        tunnel_db = _sys_usw_nh_lkup_tunnel(lchip, p_nh_param->ldp);
        if (NULL == tunnel_db)
        {
            SYS_NH_DBG_OUT(CTC_DEBUG_LEVEL_ERROR, " [Nexthop] overlay tunnel not exist!\n");
            return CTC_E_NOT_EXIST;
        }
        if (tunnel_db->ecmp_if_id)
        {
            CTC_ERROR_RETURN(sys_usw_l3if_get_ecmp_if_info(lchip, tunnel_db->ecmp_if_id, &ecmp_if));
        }
		p_nhdb->is_ecmp_nh = 0;
    }
    else
    {
        CTC_ERROR_RETURN(sys_usw_nh_get_nhinfo_by_nhid(lchip, p_nh_param->ldp, (sys_nh_info_com_t**)&p_ecmp_nh));
        if ((NULL == p_ecmp_nh) || (SYS_NH_TYPE_ECMP!= p_ecmp_nh->hdr.nh_entry_type))
        {
            SYS_NH_DBG_OUT(CTC_DEBUG_LEVEL_ERROR, " [Nexthop] not support!\n");
            return CTC_E_NOT_SUPPORT;
        }
		p_nhdb->is_ecmp_nh = 1;
    }

    /*Not CTC_NH_OL_FLAG_LDP_ECMP, can lookup tunnel by dest logic port*/
    if (!(p_nhdb->ecmp_gid && !CTC_FLAG_ISSET(p_nhdb->hdr.nh_entry_flags, SYS_NH_INFO_FLAG_USE_ECMP_IF)))
    {
        old_tunnel_db = _sys_usw_nh_lkup_tunnel(lchip, p_nhdb->dest_logic_port);
    }

    /*1. If overlay vni bind logic port, need to update the overlay vni nexthop's ldp.*/
    if (p_ol_nhdb && CTC_FLAG_ISSET(p_ol_nhdb->hdr.nh_entry_flags, SYS_NH_INFO_FLAG_LOGIC_PORT))
    {
        sys_nexthop_t dsnh;

        dsnh.offset = p_nhdb->hdr.dsnh_offset;
        dsnh.update_type = 2;
        dsnh.logic_dest_port = tunnel_db ? p_nh_param->ldp : 0;

        if (CTC_FLAG_ISSET(p_nhdb->hdr.nh_entry_flags, SYS_NH_INFO_FLAG_USE_DSNH8W))
        {
            CTC_ERROR_RETURN(sys_usw_nh_update_asic_table(lchip, SYS_NH_ENTRY_TYPE_NEXTHOP_8W, dsnh.offset, &dsnh));
        }
        else
        {
            CTC_ERROR_RETURN(sys_usw_nh_update_asic_table(lchip, SYS_NH_ENTRY_TYPE_NEXTHOP_4W, dsnh.offset, &dsnh));
        }
        p_ol_nhdb->ldp =  p_nh_param->ldp;
    }

    /*2. remove list from old db*/
    if(old_tunnel_db)
    {
        _sys_usw_nh_update_ol_tunnel_ref_info(lchip, (sys_nh_info_com_t*)p_nhdb, old_tunnel_db, 0);
    }

    /*3.1 clear dest info*/
    p_nhdb->gport = 0;
    p_nhdb->ecmp_gid = 0;
    CTC_UNSET_FLAG(p_nhdb->hdr.nh_entry_flags, SYS_NH_INFO_FLAG_USE_ECMP_IF);
    CTC_UNSET_FLAG(p_nhdb->hdr.nh_entry_flags, SYS_NH_INFO_FLAG_LOGIC_PORT);

    /*3.2 encode new nhdb*/
    if (tunnel_db)
    {
        p_nhdb->gport = tunnel_db->gport;
        p_nhdb->hdr.dsfwd1_offset = SYS_NH_OVERLAY_NEW_MODE(lchip)? 0 : p_nhdb->hdr.dsfwd1_offset;

        if (tunnel_db->ecmp_if_id)
        {
            p_nhdb->ecmp_gid = ecmp_if.hw_group_id;
            CTC_SET_FLAG(p_nhdb->hdr.nh_entry_flags, SYS_NH_INFO_FLAG_USE_ECMP_IF);
        }
        else if (tunnel_db->ul_nh_en)
        {
            sys_nh_info_ecmp_t *p_ecmp_db = NULL;
            sys_nh_info_com_t*p_nhinfo = NULL;

            CTC_ERROR_RETURN(sys_usw_nh_get_nhinfo_by_nhid(lchip, tunnel_db->ul_nhid, &p_nhinfo));
            if (SYS_NH_TYPE_ECMP == p_nhinfo->hdr.nh_entry_type)
            {
                p_ecmp_db = (sys_nh_info_ecmp_t*) p_nhinfo;
                p_nhdb->ecmp_gid = p_ecmp_db->ecmp_group_id;
                CTC_SET_FLAG(p_nhdb->hdr.nh_entry_flags, SYS_NH_INFO_FLAG_USE_ECMP_IF);
            }
            else
            {
                p_nhdb->hdr.dsfwd1_offset = p_nhinfo->hdr.dsfwd1_offset;
            }
        }
        else if (tunnel_db->arp_id && SYS_NH_OVERLAY_NEW_MODE(lchip))
        {
             p_nhdb->hdr.dsfwd1_offset = tunnel_db->ul_nhid;
        }
        _sys_usw_nh_update_ol_tunnel_ref_info(lchip, (sys_nh_info_com_t*)p_nhdb, tunnel_db, 1);
        SYS_NH_DBG_OUT(CTC_DEBUG_LEVEL_INFO, "update overlay nhid %u from ldp %u to ldp %u\n", \
        p_nhdb->hdr.nh_id, p_nhdb->dest_logic_port, p_nh_param->ldp);

        CTC_SET_FLAG(p_nhdb->hdr.nh_entry_flags, SYS_NH_INFO_FLAG_LOGIC_PORT);
    }
    else
    {
        p_nhdb->ecmp_gid = p_ecmp_nh->ecmp_group_id;
    }
    p_nhdb->dest_logic_port = p_nh_param->ldp;
    
    if (p_para->hdr.p_nh_info_ext && p_para->hdr.p_nh_info_ext->ecmp_list && tunnel_db && !tunnel_db->ecmp_if_id && (!SYS_NH_OVERLAY_NEW_MODE(lchip)))
    {
        CTC_ERROR_RETURN(_sys_usw_nh_overlay_add_dsfwd(lchip, (sys_nh_info_com_t*)p_nhdb, DRV_FROM_AT(lchip)));
    }
    if (SYS_NH_OVERLAY_NEW_MODE(lchip) && p_nhdb->hdr.dsfwd1_offset)
    {
        sys_fwd_t sys_fwd;
        uint32 dsfwd_offset = 0;

        sal_memset(&sys_fwd, 0, sizeof(sys_fwd));
        dsfwd_offset = p_nhdb->hdr.dsfwd1_offset;
        sys_fwd.offset = dsfwd_offset;
        sys_fwd.is_dsfwd1 = 1;
        sys_usw_nh_read_asic_table(lchip, SYS_NH_ENTRY_TYPE_FWD_HALF1, dsfwd_offset, &sys_fwd);
        sys_fwd.nexthop_ptr = p_nhdb->hdr.dsnh_offset;
        sys_fwd.bypass_igs_edit = CTC_FLAG_ISSET(p_nhdb->hdr.nh_entry_flags, SYS_NH_INFO_FLAG_EGRESS_EDIT_MODE);
        CTC_ERROR_RETURN(sys_usw_nh_write_asic_table(lchip, SYS_NH_ENTRY_TYPE_FWD1, dsfwd_offset, &sys_fwd));
    }

    /*4. update param*/
    if ((!CTC_FLAG_ISSET(p_nhdb->hdr.nh_entry_flags, SYS_NH_INFO_FLAG_HAVE_DSFWD) && !CTC_FLAG_ISSET(p_nhdb->hdr.nh_entry_flags, SYS_NH_INFO_FLAG_HAVE_DSFWD1))
        || SYS_NH_OVERLAY_NEW_MODE(lchip))
    {
        update_param->is_update = 1;
        update_param->nhid = p_nhdb->hdr.nh_id;
        update_param->ecmp_gid = p_nhdb->ecmp_gid;
        update_param->ldp = tunnel_db ? p_nh_param->ldp : 0;
        if(p_nhdb->ecmp_gid)/*ldp ecmp en or ecmp interface*/
        {
            update_param->destmap = SYS_ENCODE_ECMP_DESTMAP(p_nhdb->ecmp_gid);
        }
        else if(tunnel_db && tunnel_db->arp_id)
        {
            sys_nh_db_arp_t *p_arp = NULL;
            p_arp = sys_usw_nh_lkup_arp_id(lchip, tunnel_db->arp_id);
            if(p_arp)
            {
                update_param->destmap = SYS_ENCODE_ARP_DESTMAP(p_arp->destmap_profile);
            }
        }
        else if(tunnel_db)
        {
            update_param->destmap = SYS_ENCODE_DESTMAP(CTC_MAP_GPORT_TO_GCHIP(tunnel_db->gport), CTC_MAP_GPORT_TO_LPORT(tunnel_db->gport));
        }
    }

    /*set dirty flag because not update forwarding table done*/
    CTC_SET_FLAG(p_nhdb->hdr.nh_entry_flags, SYS_NH_INFO_FLAG_DIRTY);
    return CTC_E_NONE;
}

int32
sys_usw_nh_create_overlay_cb(uint8 lchip, sys_nh_param_com_t* p_com_nh_para, sys_nh_info_com_t* p_com_db)
{
    sys_nh_param_overlay_t* p_nh_param = NULL;
    sys_nh_info_overlay_t* p_nhdb = NULL;
    sys_nh_db_ol_tunnel_t* tunnel_db = NULL;
    int32 ret = 0;
    uint8 update_nh = FALSE;

    p_nh_param = (sys_nh_param_overlay_t*)(p_com_nh_para);

    p_nhdb     = (sys_nh_info_overlay_t*)(p_com_db);
    p_nhdb->hdr.nh_entry_type = SYS_NH_TYPE_OVERLAY;

    /*Need update p_nhdb->dest_logic_port before ol vni process*/
    if (!CTC_FLAG_ISSET(p_nh_param->p_overlay_nh_param->flag, CTC_NH_OL_FLAG_LDP_ECMP))
    {
        tunnel_db = _sys_usw_nh_lkup_tunnel(lchip, p_nh_param->p_overlay_nh_param->ldp);
        if (NULL == tunnel_db)
        {
            SYS_NH_DBG_OUT(CTC_DEBUG_LEVEL_ERROR, " [Nexthop] overlay tunnel not exist!\n");
            return CTC_E_NOT_EXIST;
        }

        p_nhdb->dest_logic_port = p_nh_param->p_overlay_nh_param->ldp;
        p_nhdb->gport = tunnel_db->gport;
        if (tunnel_db->ul_nh_en)
        {
            sys_nh_info_dsnh_t nh_info;
            sal_memset(&nh_info,0,sizeof(sys_nh_info_dsnh_t));
            CTC_ERROR_RETURN(_sys_usw_nh_get_nhinfo(lchip, tunnel_db->ul_nhid, &nh_info));
            p_nhdb->hdr.dsfwd1_offset = nh_info.dsfwd1_offset;
        }
        else
        {
            p_nhdb->hdr.dsfwd1_offset = tunnel_db->ul_nhid;
        }
    }
	p_nhdb->is_ecmp_nh = CTC_FLAG_ISSET(p_nh_param->p_overlay_nh_param->flag, CTC_NH_OL_FLAG_LDP_ECMP);

    if (DRV_FROM_AT(lchip) && tunnel_db && CTC_FLAG_ISSET(tunnel_db->flag, SYS_NH_OL_TUNNEL_FLAG_DSFWD))
    {
        CTC_SET_FLAG(p_nhdb->hdr.nh_entry_flags, SYS_NH_INFO_FLAG_HAVE_DSFWD1);
    }
    if (p_nh_param->p_overlay_nh_param->ol_vni)
    {
        sys_nh_info_com_t*p_nhinfo = NULL;
        sys_nexthop_t dsnh;
        CTC_ERROR_RETURN(sys_usw_nh_get_nhinfo_by_nhid(lchip, p_nh_param->p_overlay_nh_param->ol_vni, &p_nhinfo));
        if ((NULL == p_nhinfo) || (SYS_NH_TYPE_OVERLAY_VNI != p_nhinfo->hdr.nh_entry_type))
        {
            SYS_NH_DBG_OUT(CTC_DEBUG_LEVEL_ERROR, " [Nexthop] overlay vni not exist!\n");
            return CTC_E_NOT_EXIST;
        }

        sal_memset(&dsnh,0,sizeof(sys_nexthop_t));
        p_nhdb->nhid = p_nh_param->p_overlay_nh_param->ol_vni;
        p_nhdb->hdr.dsnh_offset = p_nhinfo->hdr.dsnh_offset;
        p_nhdb->hdr.nh_entry_flags |= p_nhinfo->hdr.nh_entry_flags;


        if (CTC_FLAG_ISSET(p_nhdb->hdr.nh_entry_flags, SYS_NH_INFO_FLAG_LOGIC_PORT)
            && (!CTC_FLAG_ISSET(p_nhdb->hdr.nh_entry_flags, SYS_NH_INFO_FLAG_NO_HW)))
        {
            sys_nh_info_ol_vni_t* p_ol_nhdb = (sys_nh_info_ol_vni_t*)p_nhinfo;
            if (p_ol_nhdb->ldp != 0)
            {
                return CTC_E_IN_USE;
            }

            p_ol_nhdb->ldp =  p_nhdb->dest_logic_port;
            dsnh.logic_dest_port = p_nhdb->dest_logic_port;
            CTC_SET_FLAG(dsnh.update_type, SYS_NH_UPDATE_TYPE_LDP);
            update_nh = TRUE;
        }
        if (tunnel_db && CTC_FLAG_ISSET(tunnel_db->flag,SYS_NH_OL_TUNNEL_FLAG_NSH_EN))
        {
            dsnh.strip_l2_hdr = 1;
            CTC_SET_FLAG(dsnh.update_type, SYS_NH_UPDATE_TYPE_STRIP_L2);
            update_nh = TRUE;
        }
        if (tunnel_db && tunnel_db->tunnel_type == CTC_NH_OL_TUNNEL_TYPE_NONE)
        {
             dsnh.mtu_check_en = TRUE;
             CTC_SET_FLAG(dsnh.update_type, SYS_NH_UPDATE_TYPE_MTU_CHECK);
             update_nh = TRUE;
        }
        if (TRUE == update_nh)
        {
            dsnh.offset = p_nhdb->hdr.dsnh_offset;
            if (CTC_FLAG_ISSET(p_nhdb->hdr.nh_entry_flags, SYS_NH_INFO_FLAG_USE_DSNH8W))
            {
                CTC_ERROR_RETURN(sys_usw_nh_update_asic_table(lchip, SYS_NH_ENTRY_TYPE_NEXTHOP_8W, dsnh.offset, &dsnh));
            }
            else
            {
                CTC_ERROR_RETURN(sys_usw_nh_update_asic_table(lchip, SYS_NH_ENTRY_TYPE_NEXTHOP_4W, dsnh.offset, &dsnh));
            }

        }

    }
    else
    {
        CTC_ERROR_RETURN(_sys_usw_nh_get_resolved_offset(lchip, SYS_NH_RES_OFFSET_TYPE_OL_BRIDGE_NH, &p_nhdb->hdr.dsnh_offset));
    }


    if (tunnel_db)
    {
        sys_l3if_ecmp_if_t ecmp_if = {0};

        p_nhdb->gport = tunnel_db->gport;

        if (tunnel_db->ecmp_if_id)
        {
            CTC_ERROR_RETURN(sys_usw_l3if_get_ecmp_if_info(lchip, tunnel_db->ecmp_if_id, &ecmp_if));
            p_nhdb->ecmp_gid = ecmp_if.hw_group_id;

            CTC_SET_FLAG(p_nhdb->hdr.nh_entry_flags, SYS_NH_INFO_FLAG_USE_ECMP_IF);
        }
        if (tunnel_db->ul_nh_en)
        {
            sys_nh_info_ecmp_t *p_ecmp_db = NULL;
            sys_nh_info_com_t*p_nhinfo = NULL;

            CTC_ERROR_RETURN(sys_usw_nh_get_nhinfo_by_nhid(lchip, tunnel_db->ul_nhid, &p_nhinfo));
            if (SYS_NH_TYPE_ECMP == p_nhinfo->hdr.nh_entry_type)
            {
                p_ecmp_db = (sys_nh_info_ecmp_t*) p_nhinfo;
                p_nhdb->ecmp_gid = p_ecmp_db->ecmp_group_id;
                CTC_SET_FLAG(p_nhdb->hdr.nh_entry_flags, SYS_NH_INFO_FLAG_USE_ECMP_IF);
            }
        }

        SYS_NH_DBG_OUT(CTC_DEBUG_LEVEL_INFO, "add overlay nhid %d to ldp %d\n", p_nhdb->hdr.nh_id, p_nhdb->dest_logic_port);
        _sys_usw_nh_update_ol_tunnel_ref_info(lchip, (sys_nh_info_com_t*)p_nhdb, tunnel_db, 1);
        CTC_SET_FLAG(p_nhdb->hdr.nh_entry_flags, SYS_NH_INFO_FLAG_LOGIC_PORT);
    }
    else
    {

        sys_nh_info_com_t*p_nhinfo = NULL;
        sys_nh_info_ecmp_t *p_ecmp_nh = NULL;

        CTC_ERROR_RETURN(sys_usw_nh_get_nhinfo_by_nhid(lchip, p_nh_param->p_overlay_nh_param->ldp, &p_nhinfo));
        if ((NULL == p_nhinfo) || (SYS_NH_TYPE_ECMP!= p_nhinfo->hdr.nh_entry_type))
        {
            SYS_NH_DBG_OUT(CTC_DEBUG_LEVEL_ERROR, " [Nexthop] not support!\n");
            return CTC_E_NOT_SUPPORT;
        }

        p_ecmp_nh = (sys_nh_info_ecmp_t*)p_nhinfo;
        p_nhdb->ecmp_gid = p_ecmp_nh->ecmp_group_id;
        p_nhdb->dest_logic_port = p_nh_param->p_overlay_nh_param->ldp;

    }

    return ret;
}

int32
sys_usw_nh_delete_overlay_cb(uint8 lchip, sys_nh_info_com_t* p_data, sys_nh_param_com_t* p_para)
{
    sys_nh_info_overlay_t* p_nh_info = NULL;
    sys_nh_ref_list_node_t* p_ref_node = NULL, * p_tmp_node = NULL;
    CTC_EQUAL_CHECK(SYS_NH_TYPE_OVERLAY, p_data->hdr.nh_entry_type);
    p_nh_info = (sys_nh_info_overlay_t*)(p_data);

    if(p_para->hdr.p_nh_info_ext && p_para->hdr.p_nh_info_ext->ecmp_list)
    {
        sys_usw_nh_update_ecmp_member(lchip, p_data->hdr.nh_id, SYS_NH_CHANGE_TYPE_NH_DELETE, p_para->hdr.p_nh_info_ext->ecmp_list);
        /*1. delete all reference ecmp nh*/
        p_ref_node = p_para->hdr.p_nh_info_ext->ecmp_list;

        while (p_ref_node)
        {
            /*Remove from db*/
            p_tmp_node = p_ref_node;
            p_ref_node = p_ref_node->p_next;
            mem_free(p_tmp_node);
        }
        p_para->hdr.p_nh_info_ext->ecmp_list = NULL;
    }

    if (CTC_FLAG_ISSET(p_nh_info->hdr.nh_entry_flags, SYS_NH_INFO_FLAG_HAVE_DSFWD) || 
        (CTC_FLAG_ISSET(p_nh_info->hdr.nh_entry_flags, SYS_NH_INFO_FLAG_HAVE_DSFWD1) && !SYS_NH_OVERLAY_NEW_MODE(lchip)))
    {
        sys_nh_param_dsfwd_t dsfwd_param;
        uint8 is_dsfwd1 = CTC_FLAG_ISSET(p_nh_info->hdr.nh_entry_flags, SYS_NH_INFO_FLAG_HAVE_DSFWD1);
        uint8 is_6w = CTC_FLAG_ISSET(p_nh_info->hdr.nh_entry_flags, SYS_NH_INFO_FLAG_DSFWD_IS_6W);

        sal_memset(&dsfwd_param, 0, sizeof(dsfwd_param));
        dsfwd_param.dsfwd_offset = is_dsfwd1? p_nh_info->hdr.dsfwd1_offset : p_nh_info->hdr.dsfwd_offset;
        dsfwd_param.drop_pkt = 1;
        dsfwd_param.is_6w = is_6w;
        /*Write table*/
        sys_usw_nh_write_entry_dsfwd(lchip, &dsfwd_param);
        /*Free DsFwd offset*/
        sys_usw_nh_offset_free(lchip, is_6w? (is_dsfwd1? SYS_NH_ENTRY_TYPE_FWD1 : SYS_NH_ENTRY_TYPE_FWD) : 
        (is_dsfwd1? SYS_NH_ENTRY_TYPE_FWD_HALF1 : SYS_NH_ENTRY_TYPE_FWD_HALF), 1, dsfwd_param.dsfwd_offset);
    }

    if (p_nh_info->nhid)
    {
        sys_nh_info_com_t*p_nhinfo = NULL;
        /*should not error return, because overlay vni nexthop maybe delete first*/
        CTC_ERROR_DUMP(sys_usw_nh_get_nhinfo_by_nhid(lchip, p_nh_info->nhid, &p_nhinfo));
        if ( p_nhinfo && SYS_NH_TYPE_OVERLAY_VNI == p_nhinfo->hdr.nh_entry_type)
        {
            sys_nh_info_ol_vni_t* p_ol_nhdb = NULL;
            p_ol_nhdb = (sys_nh_info_ol_vni_t*)p_nhinfo;
            p_ol_nhdb->ldp = 0;
        }
    }

    if (CTC_FLAG_ISSET(p_nh_info->hdr.nh_entry_flags, SYS_NH_INFO_FLAG_LOGIC_PORT))
    {
        sys_nh_db_ol_tunnel_t* tunnel_db = NULL;
        tunnel_db = _sys_usw_nh_lkup_tunnel(lchip,  p_nh_info->dest_logic_port);

        if (NULL == tunnel_db)
        {
            SYS_NH_DBG_OUT(CTC_DEBUG_LEVEL_ERROR, " [Nexthop] overlay tunnel not exist!\n");
            return CTC_E_NOT_EXIST;
        }

        SYS_NH_DBG_OUT(CTC_DEBUG_LEVEL_INFO, "remove overlay nhid %d from ldp %d\n", p_nh_info->hdr.nh_id, p_nh_info->dest_logic_port);
        _sys_usw_nh_update_ol_tunnel_ref_info(lchip, (sys_nh_info_com_t*)p_nh_info, tunnel_db, 0);
    }

    return CTC_E_NONE;
}

int32
sys_usw_nh_update_overlay_cb(uint8 lchip, sys_nh_info_com_t* p_nh_db,
                             sys_nh_param_com_t* p_para)
{
    int32 ret = 0;
    sys_nh_param_overlay_t* p_nh_para = NULL;
    sys_nh_info_overlay_t* p_nh_info = NULL;
    CTC_EQUAL_CHECK(SYS_NH_TYPE_OVERLAY, p_nh_db->hdr.nh_entry_type);
    p_nh_info = (sys_nh_info_overlay_t*)(p_nh_db);
    p_nh_para = (sys_nh_param_overlay_t*)(p_para);

    switch (p_nh_para->hdr.change_type)
    {
    case SYS_NH_CHANGE_TYPE_ADD_DYNTBL:
        {
            SYS_NH_DSFWD_CHK(p_nh_info->hdr.nh_entry_flags, p_para->hdr.is_dsfwd1);
            CTC_ERROR_RETURN(_sys_usw_nh_overlay_add_dsfwd(lchip,  p_nh_db, p_para->hdr.is_dsfwd1));
        }
        break;
    case SYS_NH_CHANGE_TYPE_UPDATE_FWD_ATTR:
        CTC_ERROR_RETURN(_sys_usw_nh_update_overlay_ldp(lchip, (sys_nh_info_overlay_t*)p_nh_db, (sys_nh_param_overlay_t*)p_para));
        break;
    default:
        return CTC_E_INVALID_PARAM;
    }

    if (p_para->hdr.p_nh_info_ext && p_para->hdr.p_nh_info_ext->ecmp_list)
    {
       int32 ret = 0;
       ret = (sys_usw_nh_update_ecmp_member(lchip, p_nh_para->hdr.nhid, p_nh_para->hdr.change_type, p_para->hdr.p_nh_info_ext->ecmp_list));
       if (ret)
       {
            SYS_NH_DBG_OUT(CTC_DEBUG_LEVEL_ERROR, " [Nexthop] Nexthop update for ecmp fail!, ret:%d \n", ret);
       }
    }
    if (p_para->hdr.p_nh_info_ext && (p_nh_para->hdr.change_type != SYS_NH_CHANGE_TYPE_ADD_DYNTBL))
    {
        sys_nh_ref_list_node_t* p_ref_node = NULL;
        sys_nh_info_com_t* p_nh_com = NULL;

        p_ref_node = p_para->hdr.p_nh_info_ext->aps_list;
        while (p_ref_node)
        {
            p_nh_com = p_ref_node->p_ref_nhinfo;
            sys_usw_nh_update_aps_by_nh(lchip, p_nh_com->hdr.nh_id, p_nh_info->hdr.nh_id);
            p_ref_node = p_ref_node->p_next;
        }
    }

    if (CTC_FLAG_ISSET(p_nh_db->hdr.nh_entry_flags, SYS_NH_INFO_FLAG_LOOP_USED))
    {
        CTC_ERROR_RETURN(sys_usw_nh_update_loopback_l2edit(lchip, p_nh_para->hdr.nhid, 1));
    }

    return ret;
}

int32
sys_usw_nh_get_overlay_cb(uint8 lchip, sys_nh_info_com_t* p_nh_db, void* p_para)
{
    sys_nh_info_overlay_t* p_nh_info;
    ctc_nh_overlay_param_t* p_overlay_param;

    CTC_PTR_VALID_CHECK(p_para);

    p_nh_info = (sys_nh_info_overlay_t*)(p_nh_db);
    p_overlay_param = (ctc_nh_overlay_param_t*)p_para;
    p_overlay_param->ldp = p_nh_info->dest_logic_port;
    p_overlay_param->ol_vni = p_nh_info->nhid;

    if (p_nh_info->ecmp_gid && !CTC_FLAG_ISSET(p_nh_info->hdr.nh_entry_flags, SYS_NH_INFO_FLAG_USE_ECMP_IF))
    {
        CTC_SET_FLAG(p_overlay_param->flag, CTC_NH_OL_FLAG_LDP_ECMP);
    }

    return CTC_E_NONE;
}


